digitalmars.D - nothrow by default
- Steven Schveighoffer (4/4) Jan 04 2020 Just wanted to bring this up, and not muddy the other thread.
- Mike Parker (3/8) Jan 04 2020 Seems like we'd have to. Or on/off attribute parameters.
- Walter Bright (8/17) Jan 04 2020 The first step is to add `throw` as a function attribute,
- JN (3/11) Jan 04 2020 While reusing throw might be convenient, it also makes the code
- IGotD- (2/19) Jan 04 2020 I agree, we could use "throws" instead of "throw".
- Jacob Carlborg (4/5) Jan 05 2020 Of like in D, where it has even more uses ;)
- Walter Bright (4/9) Jan 09 2020 It's a long running joke that whenever bikeshedding emerges around choic...
- Adam D. Ruppe (29/33) Jan 04 2020 There's more to code than performance. D is an all-purpose
- Walter Bright (6/19) Jan 05 2020 I've been thinking about getting rid of Error entirely, and instead usin...
- Johannes Pfau (144/149) Jan 05 2020 I totally agree to that (memory overhead, TypeInfo, support code). In
- Gregor =?UTF-8?B?TcO8Y2ts?= (37/86) Jan 05 2020 Consulting Agner Fox's microarchitecture manual, current Intel
- Johannes Pfau (35/86) Jan 05 2020 This certainly needs to be considered, however: Some languages such as g...
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (18/21) Jan 05 2020 What is "std::error"? If you mean "std::error_code" then it is
- JN (15/17) Jan 05 2020 There are reasons. A simpler language is easier to manage, both
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (20/24) Jan 05 2020 I followed the arguments they put forth when Go first launched.
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (10/11) Jan 05 2020 Oh, now I remember why I completely lost faith in the Go language
- Johannes Pfau (19/43) Jan 05 2020 As far as I know, starting from C++11 std::error_code can be extended an...
- Johannes Pfau (10/26) Jan 05 2020 Though looking at this, it seems they often throw exceptions containing
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (13/15) Jan 05 2020 Well, the fun thing about C++ is that nobody can tell you exactly
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (17/34) Jan 05 2020 Oh! I see where you are coming from. So, in D you have the
- Walter Bright (2/16) Jan 08 2020 For D, the `throw` attribute will apply to the function, not the stateme...
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (33/44) Jan 05 2020 Not only the overhead, but in real-time interrupts have to be
- rikki cattermole (15/15) Jan 05 2020 There is another exception handling mechanism which has not been
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (15/22) Jan 05 2020 When exceptions were discussed in the 80s/90s people looked at
- rikki cattermole (22/44) Jan 05 2020 Yeah there are quite a few variations on this model and I only know the
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (13/15) Jan 05 2020 But how will it be faster if it doesn't recover on the spot?
- rikki cattermole (4/9) Jan 05 2020 If you tell it to unwind, it won't.
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/10) Jan 05 2020 But I think that the concern that people have is that the codegen
- rikki cattermole (5/17) Jan 05 2020 If you use nothrow, the unwinding option would be disabled, so none of
- Jesse Phillips (5/9) Jan 10 2020 C++ remove exception specification but kept `noexcept` and
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (2/5) Jan 10 2020 No, "throw()" is not only deprecated, it is removed in C++20.
- Jesse Phillips (6/12) Jan 10 2020 My searches come up empty handed, but I'm applying the incorrect
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/9) Jan 10 2020 That's right. C++ has the "noexcept" speficier which also can be
- Jesse Phillips (4/14) Jan 10 2020 My point is c++ is not removing what is being proposed for adding
- Ola Fosheim Grostad (3/5) Jan 10 2020 What do you mean? C++ has "throw" as the default. Noexcept is the
- Jesse Phillips (3/9) Jan 11 2020 Throw was always the default, just like D.
- Jonathan M Davis (32/43) Jan 11 2020 Yes. What C++ had was a runtime attempt at what Java does. You would
- Ola Fosheim Grostad (10/16) Jan 11 2020 Not quite. In c++17 throw() was redefined to noexcept, but prior
- Ola Fosheim Grostad (3/10) Jan 11 2020 The unexpected handler ( not a regular exception).
- Walter Bright (4/4) Jan 08 2020 I'm aware that C++ is moving away from exceptions. I've been unhappy wit...
- Jonathan M Davis (22/26) Jan 09 2020 I would really hope not. IMHO, they are usually by far the best way to
- Andre Pany (8/18) Jan 09 2020 I absolutely agree with all your points. From a compiler
- Guillaume Piolat (9/23) Jan 09 2020 Yes please!
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (15/29) Jan 09 2020 I doubt Stroustrup will let that happen. Highly unlikely scenario.
- Paulo Pinto (7/39) Jan 09 2020 Unfortunately Stroustrup only has its own vote and lobbying in
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/10) Jan 09 2020 I don't use NDK, but the documentation states clearly that it
- Paulo Pinto (9/19) Jan 09 2020 Ah, the Android documentation, where Medium posts by Google
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (7/10) Jan 09 2020 But the change log does not contradict the documentation... It
- bachmeier (6/9) Jan 09 2020 +1
- Timon Gehr (8/10) Jan 09 2020 It really depends. There are situations where it may be better, but if
- Walter Bright (3/14) Jan 09 2020 Hooking the kill function is the way to do that - much better than excep...
- Timon Gehr (2/18) Jan 09 2020 Exception unwinding gives you a stack trace.
- Jonathan M Davis (4/22) Jan 09 2020 A core dump at the point of the error gives you that and more.
- Gregor =?UTF-8?B?TcO8Y2ts?= (12/14) Jan 10 2020 If only recovering core dumps was reliable these days! It has
- Walter Bright (4/5) Jan 09 2020 Stack trace printing can be independent of the exception unwinding syste...
- Mathias Lang (42/59) Jan 10 2020 Sounds like a good idea, looking forward to that DIP.
- Adam D. Ruppe (13/14) Jan 09 2020 I've seen no evidence of that whatsoever. Exceptions, like GC,
- Steven Schveighoffer (8/27) Jan 09 2020 I could see "nothrow by default" added making it an easier transition to...
- H. S. Teoh (17/27) Jan 09 2020 +1. This anti-exception sentiment I have a hard time understanding.
- Johannes Pfau (35/68) Jan 09 2020 I think everyone who wants to move away from exceptions really wants to
- H. S. Teoh (12/26) Jan 09 2020 What can you practically throw, though, if you only have 1 word of error
- Johannes Pfau (26/43) Jan 09 2020 As far as I understood Sutter's proposal it's one word of 'context'. But...
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/8) Jan 09 2020 Well, on x86 a register can be 256 bits or more... But AFAIK C++
- Johannes Pfau (8/16) Jan 09 2020 I don't know the details, but I guess that's the throw by value / catch
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/14) Jan 09 2020 It is implementation defined... in clang it uses a dedicated
- Walter Bright (14/16) Jan 09 2020 Yes. They're very costly, even in code that never throws.
- Gregor =?UTF-8?B?TcO8Y2ts?= (8/13) Jan 09 2020 The longer this discussion drags on, the more I doubt my
- Walter Bright (6/10) Jan 09 2020 You need a mechanism for jumping to the scope guard code. Can't do any c...
- Gregor =?UTF-8?B?TcO8Y2ts?= (9/23) Jan 10 2020 I played around with this as you suggested. The most concise
- Guillaume Piolat (20/24) Jan 10 2020 My understanding is that there is no overhead for functions that
- Sebastiaan Koppe (11/32) Jan 10 2020 Well said. Another option would be functional programming, with
- Paulo Pinto (8/13) Jan 09 2020 Value type exceptions as proposed by Herb and others are still
- SealabJaster (35/36) Jan 09 2020 Honestly, to me this sounds like "I don't like/use exceptions
- Joseph Rushton Wakeling (4/12) Jan 13 2020 Do you have a plan for standardizing an alternative
- rikki cattermole (5/11) Jan 04 2020 Well we could use throw, or add throws like in Java.
- Adam D. Ruppe (13/14) Jan 04 2020 this should be done regardless of defaults! for ALL these
- Jonathan M Davis (10/13) Jan 04 2020 We really should add a way to negate all of the attributes regardless of...
- Dennis (9/12) Jan 04 2020 Walter's current plan seems to be allowing the `throw` keyword to
- Adam D. Ruppe (28/35) Jan 04 2020 that looks good to me btw. I'm against the default change but
- Dennis (18/27) Jan 04 2020 So do you allow `nothrow!false` as well? Or:
- Les De Ridder (8/21) Jan 04 2020 I had this idea too (for my toy language). One potential problem
- Walter Bright (2/10) Jan 04 2020 Your comment is exactly why I'm doing this as two DIPs instead of one :-...
- apz28 (16/52) Jan 04 2020 D should make attribute keywords consistent by adding inversed
- berni44 (5/13) Jan 05 2020 I would like to have everything with @: @nothrow, @pure, @nopure
- Andrea Fontana (8/12) Jan 07 2020 nopure / novirtual / no...
- Guillaume Piolat (13/18) Jan 05 2020 WHY would nothrow by the default in the first place?
- Paul Backus (4/16) Jan 05 2020 `nothrow` by default doesn't stop you from using exceptions, it
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/11) Jan 06 2020 It kinda does: generic programming.
- Guillaume Piolat (12/31) Jan 06 2020 What do you think people will do:
Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda? -Steve
Jan 04 2020
On Saturday, 4 January 2020 at 16:05:10 UTC, Steven Schveighoffer wrote:Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda? -SteveSeems like we'd have to. Or on/off attribute parameters.
Jan 04 2020
On 1/4/2020 8:06 AM, Mike Parker wrote:On Saturday, 4 January 2020 at 16:05:10 UTC, Steven Schveighoffer wrote:The first step is to add `throw` as a function attribute, https://github.com/dlang/DIPs/pull/167 The next step will be to make nothrow the default. I have not prepared a DIP for that yet, but will. The short rationale is that exceptions being a "pay only if you use them" is a complete fraud. They're expensive to support, meaning performance programs use other ways of signalling errors and use nothrow.Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda? -SteveSeems like we'd have to. Or on/off attribute parameters.
Jan 04 2020
On Saturday, 4 January 2020 at 21:38:53 UTC, Walter Bright wrote:The first step is to add `throw` as a function attribute, https://github.com/dlang/DIPs/pull/167 The next step will be to make nothrow the default. I have not prepared a DIP for that yet, but will. The short rationale is that exceptions being a "pay only if you use them" is a complete fraud. They're expensive to support, meaning performance programs use other ways of signalling errors and use nothrow.While reusing throw might be convenient, it also makes the code less greppable. Kind of like static in C has three different uses.
Jan 04 2020
On Saturday, 4 January 2020 at 22:01:48 UTC, JN wrote:On Saturday, 4 January 2020 at 21:38:53 UTC, Walter Bright wrote:I agree, we could use "throws" instead of "throw".The first step is to add `throw` as a function attribute, https://github.com/dlang/DIPs/pull/167 The next step will be to make nothrow the default. I have not prepared a DIP for that yet, but will. The short rationale is that exceptions being a "pay only if you use them" is a complete fraud. They're expensive to support, meaning performance programs use other ways of signalling errors and use nothrow.While reusing throw might be convenient, it also makes the code less greppable. Kind of like static in C has three different uses.
Jan 04 2020
On 2020-01-04 23:01, JN wrote:Kind of like static in C has three different uses.Of like in D, where it has even more uses ;) -- /Jacob Carlborg
Jan 05 2020
On 1/5/2020 10:57 AM, Jacob Carlborg wrote:On 2020-01-04 23:01, JN wrote:It's a long running joke that whenever bikeshedding emerges around choice of a keyword, someone proposes "static". And if nobody does step up and propose it, I charge into the breach.Kind of like static in C has three different uses.Of like in D, where it has even more uses ;)
Jan 09 2020
On Saturday, 4 January 2020 at 21:38:53 UTC, Walter Bright wrote:The short rationale is that exceptions being a "pay only if you use them" is a complete fraud. They're expensive to support, meaning performance programs use other ways of signalling errors and use nothrow.There's more to code than performance. D is an all-purpose language. But anyway, D still allows throwing from nothrow, but only if it is the Error class. I presume that would not change. Would you make throw Error instead just maybe try to print the message to stderr then abort? That's not a bad idea, and is compatible with D's spec as it stands now. (the print to stderr could be problematic in some contexts but we'd just do a runtime function override in that case) Then you wouldn't need any EH framing around it. This could be done without changing the default too. But my worry on nothrow by default in general is then more D code will just become fragile since other error methods have historically meant they simply get ignored and not even reported.... barring something like those newfangled type based systems that's probably what we'd become too. BTW I have some other potential uses for those newfangled type based systems. I wouldn't object to getting some of those pieces in too - we can already do much of it. If 1) there was a way to say "ignoring the return value of this function is an error" (but that cannot apply everywhere!) and 2) be able to return from the caller from a callee (think of C macros that like `if(!SUCCEEDED(hr)) return hr` type of thing), I think we could do some real magic and have a serious alternative to exceptions. And do other useful things too. My suggestion would be to do those before attempting nothrow by default too. (then we can still vote down the default change and be left with new tools for people who want to use them :P )
Jan 04 2020
On 1/4/2020 3:06 PM, Adam D. Ruppe wrote:On Saturday, 4 January 2020 at 21:38:53 UTC, Walter Bright wrote:I've been thinking about getting rid of Error entirely, and instead using a scheme that calls a user-supplied function when things go that wrong. (Error is non-recoverable.)The short rationale is that exceptions being a "pay only if you use them" is a complete fraud. They're expensive to support, meaning performance programs use other ways of signalling errors and use nothrow.There's more to code than performance. D is an all-purpose language. But anyway, D still allows throwing from nothrow, but only if it is the Error class. I presume that would not change.But my worry on nothrow by default in general is then more D code will just become fragile since other error methods have historically meant they simply get ignored and not even reported.... barring something like those newfangled type based systems that's probably what we'd become too.It shouldn't become fragile, because the compiler will reject nothrow code on the call stack if an exception is thrown.
Jan 05 2020
Am Sat, 04 Jan 2020 13:38:53 -0800 schrieb Walter Bright:The short rationale is that exceptions being a "pay only if you use them" is a complete fraud. They're expensive to support, meaning performance programs use other ways of signalling errors and use nothrow.I totally agree to that (memory overhead, TypeInfo, support code). In addition, this overhead means that exceptions are unlikely to be supported in embedded systems, which will lead to a language ecosystem split (this effectively happened to C++). But I'm not sure if that's a good rationale for nothrow by default. We already tell users to use exceptions only for "exceptional cases" and use other error handling mechanism for common error paths. With this change, we'd force users even stronger to use other error handling methods. But those don't have language support and we're back to C times, where users forget to check error codes, ... Herb Sutter came to the same conclusion recently and proposed an alternative exception implementation for C++. If you didn't see this already Walter, it well worth to have a look: https://www.youtube.com/watch?v=os7cqJ5qlzo https://www.youtube.com/watch?v=ARYP83yNAWk I'll summarize the idea here, as far as I remember and adapted to D terminology / context: Rationale: There are two common ways to handle errors: Backtrace/ exception based and error code based. Both have drawbacks (exceptions: performance, memory / implementation overhead, not supported in embedded systems) (error codes: not "bubbling up" the call stack automatically, no way to force user to check, so might accidentially ignore errors). Solution: 1) Implement exceptions like return codes: Each function which can throw returns a union ValueOrError{T result; Error err}. The discriminator bit is stored as a CPU flag register which is not used across function calls (e.g. the OVERFLOW status bit). Error is a two-word error code: Error {size_t code; size_t context}. This explains the ABI, for now nothing of this is user-visible. Whenever a call to a function which might throw occurs and there's a try-catch handler, insert this code after the call: if (OVERFLOW == 1) goto catch_handler; If there's no catch handler installed and a function might throw: if (OVERFLOW == 1) return; The return value is already in the return registers. Therefore propagating error codes upwards is cheap. The benefits now are simple: Implementation code / memory overhead is almost zero, no TypeInfo required, trivial to implement on embedded systems, .... At the same time, exceptions bubble up properly so they can't be accidentially unhandled. The runtime overhead in the success case is obviously higher than for some exception systems, but it's only a simple conditional jump. It is cheaper than manual error codes, as we reuse the same registers for return value and error code, benefitting register allocation. Error propagation also uses the same registers in all functions and is therefore very efficient. For D / legacy exception support, you just store code=LEGACY_EXCEPTION, context=Exception pointer. To allocate the error codes in a distributed way and to check for different error types, we can simply store dummy values in the data segment to get unique addresses: ubyte OutOfMemoryError; ... ; if (ret.code == &OutOfMemoryError)... 2) (Optional): Herb arguest that because of throw ... it is easy to spot where an exception originates, but it's more difficult to find where an exception was propagated. As a solution, whenever calling a throws function, the calls should be preceeded by throw: auto value = throw myThrowingFunction(); Here throw does essentially this: If myThrowingFunction threw, rethrow the exception. Otherwise return the return value. 3) (C++ specific): Error codes instead of exceptions, "Type based Errors": I don't really know what is meant by this "Type-Based Errors" terminology, seems to be a C++ marketing thing ("we do everything with types now")... The important takeaway is to not allocate complex exception objects. Use the error code and context value. If really more context than one word is necessary, it's still possible to stuff a pointer into context. 4) (D-specific): Not mentioned in the C++ proposal, but some interesting extension: A more local error handling solution: In D we have try/catch if we want to handle errors from multiple functions calls and scope(failure) to handle all errors in a function. But fine-grain error handling is annoying: ----------------- try foo(); catch(Exception e) { switch(e) { case DNS_ERROR: writeln("Dns erorr"); retry(); case ... } } // success code here ----------------- quite some overhead here. Traditional error codes are better here: ----------------- T ret; switch(ret = foo()) { case ...: return; } // Success here ----------------- Maybe we could formalize this: We can expose the ValueOrError union to the user. foo() then returns ValueOrError!T and the 'throw' in 2) could simply check for errors, otherwise return ValueOrError!(T).value. We could then add an opCast(bool) overload to ValueOrError to make if (value) work, add a function to check/abort and convert ValueOrError.getValue and overload the switch statement on this: ----------------- auto res = foo(); switch(res.error) { case DNS_ERROR: return; default: // Other error return; } // With flow-typing we'd know that ValueOrError is no error here. Without, we have to explicitly convert the type somehow: T value; // Switch magically overloaded on error / value tuple switch ((value, error) = foo()) { writeln(error); return; } writeln(value); ----------------- However, with these ideas in 4), we'd have to force the user somehow to check the error bit and make the value only accessible after the check. Caveats: * I have not thought about how exception chaining fits into all this. * These exceptions do not naturally propagate through foreign language interfaces, although I think we don't guarantee this in D right now either. If there's any real interest in this, I'd be happy to write a proper DIP(s) for it (I think 1, 2 and 4 are all independent DIPs). If C++ really gets this, we might have to support it anyway for C++ interop. PS: Back to the original question: nothrow by default: With 2) every function which might throw and where errors are not handled locally needs to have throw in front of the function call. Obviously, we don't want to have too many of these, so nothrow functions should be the common case. And the common case should be the default, so nothrow by default also makes sense in this context. -- Johannes
Jan 05 2020
On Sunday, 5 January 2020 at 10:32:23 UTC, Johannes Pfau wrote:The benefits now are simple: Implementation code / memory overhead is almost zero, no TypeInfo required, trivial to implement on embedded systems, .... At the same time, exceptions bubble up properly so they can't be accidentially unhandled. The runtime overhead in the success case is obviously higher than for some exception systems, but it's only a simple conditional jump. It is cheaper than manual error codes, as we reuse the same registers for return value and error code, benefitting register allocation. Error propagation also uses the same registers in all functions and is therefore very efficient.Consulting Agner Fox's microarchitecture manual, current Intel CPUs still take a performance hit when encountering code with a high density of conditional branches. They are sensitive to the number of branch instructions per cache line. I've seen GCC pad code with NOPs for that reason. AMD's branch predictor, on the other hand, is described as perceptron that fares better with repeated patterns of branches and isn't tied to the cache. Branch mispredictions still hurt a lot (~20 cycles). All microcontrollers I'm familiar stall their pipeline on every branch if they're pipelined at all. Moving error handling overhead back into the common/fast code path is a step backwards in those cases where performance matters. Desktops and servers can absolutely afford to have the data overhead associated with exceptions and they benefit the most from out of band error handling. It would be nice to have choice here.2) (Optional): Herb arguest that because of throw ... it is easy to spot where an exception originates, but it's more difficult to find where an exception was propagated. As a solution, whenever calling a throws function, the calls should be preceeded by throw: auto value = throw myThrowingFunction(); Here throw does essentially this: If myThrowingFunction threw, rethrow the exception. Otherwise return the return value.I hope that this doesn't require code to have a throw keyword in every other line. Imagine outer functions of some algorithm where the inner functions have a lot of opportunity to fail: auto result1 = throw step1(); auto result2 = throw step2(result1); // etc... The value of the keyword decreases rapidly with the number of occurrences.3) (C++ specific): Error codes instead of exceptions, "Type based Errors": I don't really know what is meant by this "Type-Based Errors" terminology, seems to be a C++ marketing thing ("we do everything with types now")... The important takeaway is to not allocate complex exception objects. Use the error code and context value. If really more context than one word is necessary, it's still possible to stuff a pointer into context.There's recently been a talk about error types in C++ and their evolution: https://www.youtube.com/watch?v=coBz_CQ1tJ8& As with everything in C++, there's a lot of complexity.Caveats: * I have not thought about how exception chaining fits into all this.This would require an exception to be allocated that starts the chain. The error return value could then turn into a pointer to that value. That's similar to how the C++ proposals return exceptions in error value returning functions.* These exceptions do not naturally propagate through foreign language interfaces, although I think we don't guarantee this in D right now either.D exceptions and C++ exception implementations are currently quite incompatible, but the documentation still states that that is an eventual goal of D: https://dlang.org/spec/cpp_interface.html#exception-handling
Jan 05 2020
Am Sun, 05 Jan 2020 12:27:11 +0000 schrieb Gregor Mückl:Consulting Agner Fox's microarchitecture manual, current Intel CPUs still take a performance hit when encountering code with a high density of conditional branches. They are sensitive to the number of branch instructions per cache line. I've seen GCC pad code with NOPs for that reason. AMD's branch predictor, on the other hand, is described as perceptron that fares better with repeated patterns of branches and isn't tied to the cache. Branch mispredictions still hurt a lot (~20 cycles). All microcontrollers I'm familiar stall their pipeline on every branch if they're pipelined at all. Moving error handling overhead back into the common/fast code path is a step backwards in those cases where performance matters. Desktops and servers can absolutely afford to have the data overhead associated with exceptions and they benefit the most from out of band error handling. It would be nice to have choice here.This certainly needs to be considered, however: Some languages such as go (server!) and C++ with std:error decided to go for error codes for error handling. Here the proposal is more efficient and much more safe than completely manual error code handling. Also exceptions can be up to 1000 times slower for the error path. So you can now start doing statistics how exceptional your error path has to be for exception handling to be more effective. And if the error path is taken very rarely, it is easy for the branch predictor to predict. I have to admit, if you get rid of allocation and all the other exception complexity, the overhead of pure backtracing is probably less. But it's still significantly more than a simple branch. Regarding microcontrollers, how many microcontroller projects use full backtracing implementations? Every codebase I've ever seen for these systems uses error codes anyway. So Sutter's proposal would not affect the performance of these systems at all. The point about increased branch instruction density hurting the success path might still apply. But it's highly dependent on the relative amount of functions which actually use error handling. So it seems difficult to assess the impact without benchmarking an implementation.I hope that this doesn't require code to have a throw keyword in every other line. Imagine outer functions of some algorithm where the inner functions have a lot of opportunity to fail: auto result1 = throw step1(); auto result2 = throw step2(result1); // etc... The value of the keyword decreases rapidly with the number of occurrences.That's certainly true. I think we need some kind of statistics on the distribution of nothrow / throw functions in D code here. This would also be useful information to make a more informed decision about the throw/ nothrow defaults. As long as calling throwing functions is uncommon, making the call explicit should be beneficial.There's recently been a talk about error types in C++ and their evolution: https://www.youtube.com/watch?v=coBz_CQ1tJ8& As with everything in C++, there's a lot of complexity.Thanks for the link, I'll have a look at this.This is actually similar to the way exception chaining is implemented in GDC right now. So that's probably not a hard problem.Caveats: * I have not thought about how exception chaining fits into all this.This would require an exception to be allocated that starts the chain. The error return value could then turn into a pointer to that value. That's similar to how the C++ proposals return exceptions in error value returning functions.I was actually more thinking of a call chain like this: dfun1 => cppfun => dfun2. If dfun2 throws an exception, you can actually catch the exception in dfun1 (even though you do not know if cppfun properly ran its destructors). However, if dfun2 returns an error code union + flag, this error information does not propagate to dfun1. -- Johannes* These exceptions do not naturally propagate through foreign language interfaces, although I think we don't guarantee this in D right now either.D exceptions and C++ exception implementations are currently quite incompatible, but the documentation still states that that is an eventual goal of D: https://dlang.org/spec/cpp_interface.html#exception-handling
Jan 05 2020
On Sunday, 5 January 2020 at 15:02:59 UTC, Johannes Pfau wrote:This certainly needs to be considered, however: Some languages such as go (server!) and C++ with std:error decided to go for error codes for error handling.What is "std::error"? If you mean "std::error_code" then it is for wrapping OS error codes like the ones from Unix. (You can throw it if want...) The error handling regime in Go is not something anyone should copy. The language lacks features that makes code maintainable over time. Lots of pointless error-handling boilerplate noise. Also, they didn't do it because of speed, Go is not that fast. They did it for no scientific reason, just their own (bad) taste, but they later found that they had to add the awful hack that resembles exceptions, but is a lot more ugly and clunky. Although you can hack it to do what you want, in a rather type-unsafe way. So I do that, but Go is the error handling scarecrow of modern languages... You don't want to write big programs in Go. Go I useful for small web services because it has a decent runtime, stable language design, few compiler bugs, decent web-service libraries and cloud support. However, the language itself is pretty primitive (for no good reason).
Jan 05 2020
On Sunday, 5 January 2020 at 19:53:59 UTC, Ola Fosheim Grøstad wrote:However, the language itself is pretty primitive (for no good reason).There are reasons. A simpler language is easier to manage, both for compiler and for IDEs, potentially allowing faster development time and better performance/reliability, even if it doesn't have powerful features. From Go's FAQ: **Why does Go not have feature X?** Every language contains novel features and omits someone's favorite feature. Go was designed with an eye on felicity of programming, speed of compilation, orthogonality of concepts, and the need to support features such as concurrency and garbage collection. Your favorite feature may be missing because it doesn't fit, because it affects compilation speed or clarity of design, or because it would make the fundamental system model too difficult.
Jan 05 2020
On Sunday, 5 January 2020 at 20:22:14 UTC, JN wrote:There are reasons. A simpler language is easier to manage, both for compiler and for IDEs, potentially allowing faster development time and better performance/reliability, even if it doesn't have powerful features. From Go's FAQ:I followed the arguments they put forth when Go first launched. People complained loudly about not having good abstraction and error handling mechanisms and they (the language designers) were rather religious about their position. They are Unix/C/Plan9 people. Go as a language design does not provide better reliability, you are not forced to handle errors properly. (Unix APIs are also not great for correctness.) The Go designers found that they had to add "exceptions". They found that they had to add "generics". Coming in Go2. When your function gets 100% larger because of the language lacks basic abstraction/error handling features then that does not improve correctness... Hiding the basic logic flow in error handling branching is not good for correctness or maintenance either. They have marketed what it lacks as "good design". Go-people parrot it as good design, but that does not make it true... Fortunately, most server code is simple I/O. So it is OK for writing a service, but not great, although the runtime makes up for it. Somewhat.
Jan 05 2020
On Sunday, 5 January 2020 at 20:22:14 UTC, JN wrote:**Why does Go not have feature X?**Oh, now I remember why I completely lost faith in the Go language designers. I asked for asserts, and they absolutely refused and said that they would never add asserts because some programmers might use it to handle errors... therefore nobody should have them. So, not having exceptions makes error-handling so tedious that some programmers will use asserts to check errors... therefore Go also could not have asserts. Completely backwards and arcane mentality? I think so.
Jan 05 2020
Am Sun, 05 Jan 2020 19:53:59 +0000 schrieb Ola Fosheim Grøstad:On Sunday, 5 January 2020 at 15:02:59 UTC, Johannes Pfau wrote:As far as I know, starting from C++11 std::error_code can be extended and has been used in newer updates to the C++ standard library instead of exceptions. (e.g. there is now a future error category). See https:// akrzemi1.wordpress.com/2017/07/12/your-own-error-code/ std::expected / boost outcome proposals are also related. There certainly is a trend away from exception to error-code like systems. But I have to admit, I don't know much about C++, so maybe those are bad / non- representative examples.This certainly needs to be considered, however: Some languages such as go (server!) and C++ with std:error decided to go for error codes for error handling.What is "std::error"? If you mean "std::error_code" then it is for wrapping OS error codes like the ones from Unix. (You can throw it if want...)The error handling regime in Go is not something anyone should copy. The language lacks features that makes code maintainable over time. Lots of pointless error-handling boilerplate noise. Also, they didn't do it because of speed, Go is not that fast. They did it for no scientific reason, just their own (bad) taste, but they later found that they had to add the awful hack that resembles exceptions, but is a lot more ugly and clunky. Although you can hack it to do what you want, in a rather type-unsafe way. So I do that, but Go is the error handling scarecrow of modern languages... You don't want to write big programs in Go. Go I useful for small web services because it has a decent runtime, stable language design, few compiler bugs, decent web-service libraries and cloud support. However, the language itself is pretty primitive (for no good reason).That's probably true. Still, you can do a exception system with the same user facing API but without backtracing. So you get rid of unwind tables and the backtrace support code making the failure case faster while reducing the performance of the succes case. In that case the only question is whether the performance difference is important for some applications. Of course, ideally which backend implementation is used could be an implementation detail. -- Johannes
Jan 05 2020
Am Sun, 05 Jan 2020 21:06:00 +0000 schrieb Johannes Pfau:Am Sun, 05 Jan 2020 19:53:59 +0000 schrieb Ola Fosheim Grøstad:Though looking at this, it seems they often throw exceptions containing these error codes? However, Herb Sutter explicitly mentioned std::error_code as causing fragmentation in C++ (e.g. std::filesystem uses both exceptions and error_code), see slide at https://youtu.be/ os7cqJ5qlzo?t=764 So I assumed error codes are common in modern C++, but I don't really have first-hand experience to say for sure. -- JohannesOn Sunday, 5 January 2020 at 15:02:59 UTC, Johannes Pfau wrote:As far as I know, starting from C++11 std::error_code can be extended and has been used in newer updates to the C++ standard library instead of exceptions. (e.g. there is now a future error category). See https:// akrzemi1.wordpress.com/2017/07/12/your-own-error-code/This certainly needs to be considered, however: Some languages such as go (server!) and C++ with std:error decided to go for error codes for error handling.What is "std::error"? If you mean "std::error_code" then it is for wrapping OS error codes like the ones from Unix. (You can throw it if want...)
Jan 05 2020
On Sunday, 5 January 2020 at 21:12:39 UTC, Johannes Pfau wrote:So I assumed error codes are common in modern C++, but I don't really have first-hand experience to say for sure.Well, the fun thing about C++ is that nobody can tell you exactly what modern C++ is, but it is easy to point out old-style C++... Many patterns are outdated, but there are many different "modern" patterns as well... The C++ community is split down the middle. Those that use C++ as designed (with exceptions), and those that use C++ as a C replacement (without exceptions). I guess some modules have been viewed as low level and thus have been designed to be usable by both groups. You also have additions like std::optional that can be used if you turn off exceptions. I'm sure we see more in that direction if Rust becomes more popular...
Jan 05 2020
On Sunday, 5 January 2020 at 21:06:00 UTC, Johannes Pfau wrote:As far as I know, starting from C++11 std::error_code can be extended and has been used in newer updates to the C++ standard library instead of exceptions. (e.g. there is now a future error category). See https:// akrzemi1.wordpress.com/2017/07/12/your-own-error-code/ std::expected / boost outcome proposals are also related. There certainly is a trend away from exception to error-code like systems. But I have to admit, I don't know much about C++, so maybe those are bad / non- representative examples.Oh! I see where you are coming from. So, in D you have the problem that large sections of the standard library cannot be used with out the GC. In C++ you have a similar problem with not being able to use most of the standard library (safely) if you turn off exceptions. So they have both tried to make the code gen for exception code better, but I believe there also is an effort to make more of the standard library usable for those who choose to turn off exceptions.That's probably true. Still, you can do a exception system with the same user facing API but without backtracing. So you get rid of unwind tables and the backtrace support code making the failure case faster while reducing the performance of the succes case. In that case the only question is whether the performance difference is important for some applications. Of course, ideally which backend implementation is used could be an implementation detail.There are absolutely many paths to new (or old) interesting error handling/resource management mechanisms. E.g. some research language for system programming have used arenas with compiler backing and other have explored other alternatives. But D has to figure out how to relate to C++. Doing the opposite of C++ just on the syntax level (throw/nothrow/noexcept), does not make much sense if D is to attract C++ developers. It has to be substantially better to have an impact, like you suggest.
Jan 05 2020
On 1/5/2020 4:27 AM, Gregor Mückl wrote:It would be nice to have choice here.For D, the `throw` attribute will apply to the function, not the statement.2) (Optional): Herb arguest that because of throw ... it is easy to spot where an exception originates, but it's more difficult to find where an exception was propagated. As a solution, whenever calling a throws function, the calls should be preceeded by throw: auto value = throw myThrowingFunction(); Here throw does essentially this: If myThrowingFunction threw, rethrow the exception. Otherwise return the return value.I hope that this doesn't require code to have a throw keyword in every other line.
Jan 08 2020
On Sunday, 5 January 2020 at 10:32:23 UTC, Johannes Pfau wrote:I totally agree to that (memory overhead, TypeInfo, support code). In addition, this overhead means that exceptions are unlikely to be supported in embedded systems, which will lead to a language ecosystem split (this effectively happened to C++).Not only the overhead, but in real-time interrupts have to be 100% sure that no code triggers a system call or ends up waiting for a lock to be released. Also in applications (e.g. audio drivers). Seems to me that static analysis could be used for this, the same way nogc works. I think D should look at something more comprehensive regarding managing resources instead of these tweaks that have very little positive impact.Rationale: There are two common ways to handle errors: Backtrace/ exception based and error code based. Both have drawbacks (exceptions: performance, memory / implementation overhead, not supported in embedded systems) (error codes: not "bubbling up" the call stack automatically, no way to force user to check, so might accidentially ignore errors).Yes, but back-tracing with error codes can be slow. RAII can be slow. You can have exceptions in real time systems (setjmp), basically just reload saved register values that sets the stack pointer and program counter to a consistent state. So an exception (set jump) can be much faster than back-tracing if you have no RAII on the call stack and all allocations are done either on the stack and all other resources are bound to an Arena-allocator/manager at the landing pad. What makes C++-style exceptions slow is the unwind library's cross-language design and the surrounding RAII philosophy... Exceptions, as a concept, are not necessarily slow. If you can just set the stack pointer and jump to the landing pad then you have something much faster than returning/unwinding down the call stack for deep call-trees. But since D should be able to call C++ code that won't work, and nothrow by default will not help with C++. So to get anywhere you first have to figure out how to interact with C++ code. If you call C++ code then you cannot assume that it won't throw, unless it is marked as "noexcept" and even if it is noexcept it still can have landing pads. So you cannot bypass the unwinding library... Am I right? In terms of supporting generic programming it is better to have "possibly throws" everywhere.
Jan 05 2020
There is another exception handling mechanism which has not been seriously suggested up until now, but now may be the time for it. The basics are the same. Up in the call tree is some sort of handler registered. This will be a delegate. It returns an enum. This enum will typically have members like error, prematureReturn, continue, and unroll. The way this delegate get used is that it gets matched bottom up (like we do now), in the thrower. The throw statement will match the delegate to the exception, call the delegate and then proceed to do whatever that function tells it to do. It could assert out, return Return.init, continue doing stuff (which may lead to asserting out), or unroll to the handling point (which is what we want to avoid). As this would be D only, it could be a lot cheaper and may not exhibit the same limitations as we have now.
Jan 05 2020
On Sunday, 5 January 2020 at 13:12:47 UTC, rikki cattermole wrote:The way this delegate get used is that it gets matched bottom up (like we do now), in the thrower. The throw statement will match the delegate to the exception, call the delegate and then proceed to do whatever that function tells it to do. It could assert out, return Return.init, continue doing stuff (which may lead to asserting out), or unroll to the handling point (which is what we want to avoid).When exceptions were discussed in the 80s/90s people looked at recovery that kinda works they way you describe. So when a function fails it can stop and then let some other unit fix the problem and then resume where it halted. I guess you could do this with coroutines in some elegant fashion. The problem is really writing the code in a recoverable fashion, which most programmers seem to not be able to do, hence the current "transactional" model of unrolling and the retrying from scratch. With transactional memory and perhaps even a more generic transactional concept that also covers external resources then you could have complete unrolling without programmer intervention (so that no RAII/destructors have to be written). But that is rather advanced... and more like research topic I guess.
Jan 05 2020
On 06/01/2020 2:21 AM, Ola Fosheim Grøstad wrote:On Sunday, 5 January 2020 at 13:12:47 UTC, rikki cattermole wrote:Yeah there are quite a few variations on this model and I only know the basics. I don't think we would need the exception handler delegate to "fix" the problem per-say, only determine what the user code wants to do. But perhaps a company like WekaIO has a use case I haven't thought about that would make this a killer feature for them.The way this delegate get used is that it gets matched bottom up (like we do now), in the thrower. The throw statement will match the delegate to the exception, call the delegate and then proceed to do whatever that function tells it to do. It could assert out, return Return.init, continue doing stuff (which may lead to asserting out), or unroll to the handling point (which is what we want to avoid).When exceptions were discussed in the 80s/90s people looked at recovery that kinda works they way you describe. So when a function fails it can stop and then let some other unit fix the problem and then resume where it halted.I guess you could do this with coroutines in some elegant fashion. The problem is really writing the code in a recoverable fashion, which most programmers seem to not be able to do, hence the current "transactional" model of unrolling and the retrying from scratch.Yes. IMO the two most likely used out comes would be rethrows or to return prematurely.With transactional memory and perhaps even a more generic transactional concept that also covers external resources then you could have complete unrolling without programmer intervention (so that no RAII/destructors have to be written). But that is rather advanced... and more like research topic I guess.When I described unrolling, I do mean unrolling using the exception handling mechanism. Just an idea: try { } catch(Exception e) { // continue throw; // continue; // break; } Any of the statements in the catch block would opt-in to this new mechanism, with the default remaining what we have now. We could also support some sort of typedef'd tuple or random struct's, that would force into this new mechanism and default to assert(0);
Jan 05 2020
On Sunday, 5 January 2020 at 13:32:24 UTC, rikki cattermole wrote:When I described unrolling, I do mean unrolling using the exception handling mechanism.But how will it be faster if it doesn't recover on the spot? So, if you have an object like the one you propose you could also make that object a resource manager (held in thread local storage). Then you don' t have to unwind. You just free all the resources the object is holding onto, set a new stack pointer and load in the landing pad in the program counter. But you need a smart compiler, strict type checking and a cleverly designed runtime. Anyway, C++ deprecated the the "throw" specifier in C++11 and removed it completely in C++20. Not sure why D users will be more accepting of having to specify "throw". More patient user base perhaps.
Jan 05 2020
On 06/01/2020 2:51 AM, Ola Fosheim Grøstad wrote:On Sunday, 5 January 2020 at 13:32:24 UTC, rikki cattermole wrote:If you tell it to unwind, it won't. The idea is to either assert out or return from the erroring function and let the parent figure it out.When I described unrolling, I do mean unrolling using the exception handling mechanism.But how will it be faster if it doesn't recover on the spot?
Jan 05 2020
On Sunday, 5 January 2020 at 13:56:14 UTC, rikki cattermole wrote:On 06/01/2020 2:51 AM, Ola Fosheim Grøstad wrote:But I think that the concern that people have is that the codegen will generate landingpads for all stackframes that either catch exceptions or call destructors. And that those landingpads make writing the code optimization stages more difficult.But how will it be faster if it doesn't recover on the spot?If you tell it to unwind, it won't. The idea is to either assert out or return from the erroring function and let the parent figure it out.
Jan 05 2020
On 06/01/2020 3:27 AM, Ola Fosheim Grøstad wrote:On Sunday, 5 January 2020 at 13:56:14 UTC, rikki cattermole wrote:If you use nothrow, the unwinding option would be disabled, so none of that would be a problem. But you would lose the ability unwind, I don't think we can do anything about that right now.On 06/01/2020 2:51 AM, Ola Fosheim Grøstad wrote:But I think that the concern that people have is that the codegen will generate landingpads for all stackframes that either catch exceptions or call destructors. And that those landingpads make writing the code optimization stages more difficult.But how will it be faster if it doesn't recover on the spot?If you tell it to unwind, it won't. The idea is to either assert out or return from the erroring function and let the parent figure it out.
Jan 05 2020
On Sunday, 5 January 2020 at 13:51:33 UTC, Ola Fosheim Grøstad wrote:Anyway, C++ deprecated the the "throw" specifier in C++11 and removed it completely in C++20. Not sure why D users will be more accepting of having to specify "throw". More patient user base perhaps.C++ remove exception specification but kept `noexcept` and `throw()` both of which behave like the desired `throw` discussed here for D.
Jan 10 2020
On Friday, 10 January 2020 at 15:43:35 UTC, Jesse Phillips wrote:C++ remove exception specification but kept `noexcept` and `throw()` both of which behave like the desired `throw` discussed here for D.No, "throw()" is not only deprecated, it is removed in C++20.
Jan 10 2020
On Friday, 10 January 2020 at 16:01:26 UTC, Ola Fosheim Grøstad wrote:On Friday, 10 January 2020 at 15:43:35 UTC, Jesse Phillips wrote:My searches come up empty handed, but I'm applying the incorrect semantics to throw(), that is equal to the noexcept semantics which is equal to D's nothrow. I don't see the same proposed semantics in c++.C++ remove exception specification but kept `noexcept` and `throw()` both of which behave like the desired `throw` discussed here for D.No, "throw()" is not only deprecated, it is removed in C++20.
Jan 10 2020
On Friday, 10 January 2020 at 16:32:33 UTC, Jesse Phillips wrote:My searches come up empty handed, but I'm applying the incorrect semantics to throw(), that is equal to the noexcept semantics which is equal to D's nothrow. I don't see the same proposed semantics in c++.That's right. C++ has the "noexcept" speficier which also can be expressed as "noexcept(true)" in generic programming, but the default is "noexcept(false)" (with a few special cases). The C++ semantics is the opposite of nothrow by default.
Jan 10 2020
On Friday, 10 January 2020 at 16:46:32 UTC, Ola Fosheim Grøstad wrote:On Friday, 10 January 2020 at 16:32:33 UTC, Jesse Phillips wrote:My point is c++ is not removing what is being proposed for adding 'throw'My searches come up empty handed, but I'm applying the incorrect semantics to throw(), that is equal to the noexcept semantics which is equal to D's nothrow. I don't see the same proposed semantics in c++.That's right. C++ has the "noexcept" speficier which also can be expressed as "noexcept(true)" in generic programming, but the default is "noexcept(false)" (with a few special cases). The C++ semantics is the opposite of nothrow by default.
Jan 10 2020
On Friday, 10 January 2020 at 22:00:08 UTC, Jesse Phillips wrote:My point is c++ is not removing what is being proposed for adding 'throw'What do you mean? C++ has "throw" as the default. Noexcept is the outlier, so yes, they did remove it.
Jan 10 2020
On Friday, 10 January 2020 at 22:27:35 UTC, Ola Fosheim Grostad wrote:On Friday, 10 January 2020 at 22:00:08 UTC, Jesse Phillips wrote:Throw was always the default, just like D.My point is c++ is not removing what is being proposed for adding 'throw'What do you mean? C++ has "throw" as the default. Noexcept is the outlier, so yes, they did remove it.
Jan 11 2020
On Saturday, January 11, 2020 1:02:09 AM MST Jesse Phillips via Digitalmars- d wrote:On Friday, 10 January 2020 at 22:27:35 UTC, Ola Fosheim Grostad wrote:Yes. What C++ had was a runtime attempt at what Java does. You would optionally specify which exceptions could be thrown from a function (whereas in Java, it's mandatory to specify). So, if you had int foo(std::string bar); then the function could throw anything (even stuff like int, since C++ didn't restrict exception types at all), whereas if you had int foo(std::string bar) throw(MyExceptionType); then the function could only throw the type MyExceptionType. This is similar to Java, but whereas Java statically restricts what can be thrown, C++ did it at runtime. So, there were _zero_ compile-time checks that no other types were thrown, and if something else were thrown, it would basically kill your program. This was a terrible, terrible idea, which is why they started phasing it out with C++11. The one aspect of it which was arguably a good idea was throw() - e.g. int foo(std::string bar) throw(); which was their version of nothrow. It was still a runtime check, but there are cases where you absolutely can't have an exception be thrown (destructors being the primary use case), so while it's still bad that it's a runtime check, it's deemed better to kill the program in such a case than to continue. So, C++11 introduced noexcept to replace it. e.g. int foo(std::string bar) noexcept; noexcept does the same thing as throw(), but throw() is going away along with throw(MyExceptionType). So, with throw(...) removed, C++ ends up with a solution similar to what we currently have in D except that noexcept is a runtime check, whereas nothrow is a compile-time check, and D lets non-Exception Throwables be thrown anyway, which can get a bit weird. If we were to add throw as a function attribute and make nothrow the default, that would be the opposite of what C++ has, just like it's the opposite of what we currently have in D. - Jonathan M DavisOn Friday, 10 January 2020 at 22:00:08 UTC, Jesse Phillips wrote:Throw was always the default, just like D.My point is c++ is not removing what is being proposed for adding 'throw'What do you mean? C++ has "throw" as the default. Noexcept is the outlier, so yes, they did remove it.
Jan 11 2020
On Saturday, 11 January 2020 at 14:38:50 UTC, Jonathan M Davis wrote:noexcept does the same thing as throw(), but throw() is going away alongNot quite. In c++17 throw() was redefined to noexcept, but prior to this it should unwind the stack with unexpected as the exception. Noexcept may or may not unwind the stack and allow optimizations.with throw(MyExceptionType). So, with throw(...) removed, C++ ends up with a solution similar to what we currently have in D except that noexcept is aNoexcept is now part of the function type and you can test expressions with the noexcept operator. I believe it is more to support generic programming and optimization than to prevent bad behaviour.
Jan 11 2020
On Saturday, 11 January 2020 at 22:57:22 UTC, Ola Fosheim Grostad wrote:On Saturday, 11 January 2020 at 14:38:50 UTC, Jonathan M Davis wrote:The unexpected handler ( not a regular exception).noexcept does the same thing as throw(), but throw() is going away alongNot quite. In c++17 throw() was redefined to noexcept, but prior to this it should unwind the stack with unexpected as the exception.
Jan 11 2020
I'm aware that C++ is moving away from exceptions. I've been unhappy with exceptions for some time now (DMD doesn't use them for performance reasons), and C++ has evidently come to the same conclusion. I expect that exceptions will soon become a legacy feature.
Jan 08 2020
On Wednesday, January 8, 2020 11:26:41 PM MST Walter Bright via Digitalmars- d wrote:I'm aware that C++ is moving away from exceptions. I've been unhappy with exceptions for some time now (DMD doesn't use them for performance reasons), and C++ has evidently come to the same conclusion. I expect that exceptions will soon become a legacy feature.I would really hope not. IMHO, they are usually by far the best way to handle error conditions in a program. Anything else results in you having to deal with the various possible error conditions when the function returns, which is _far_ more unwieldy and much more error-prone. It also doesn't work well with function call chaining. By using exceptions, you eliminate the need to have error-handling code everywhere in your program, and unlike stuff like error codes, exceptions don't get eaten unless the programmer specifically puts in effort to make it happen. I wouldn't mind us getting rid of Error in favor of killing the program on the spot, since that's actually better for debugging, and it avoids the whole argument about whether it's okay to catch Errors. However, Exceptions are for runtime conditions that aren't necessarily a bug, and they can often be recovered from. So, being able to throw Exceptions is extremely useful, and the fact that they don't force you to check for error conditions everyewhere and instead just handle them in the spots where it makes sense to handle them makes the code _way_ cleaner. IMHO, having nothrow be the default would be terrible, and trying to treat exceptions as a legacy feature would be even worse. - Jonathan M Davis
Jan 09 2020
On Thursday, 9 January 2020 at 10:32:25 UTC, Jonathan M Davis wrote:On Wednesday, January 8, 2020 11:26:41 PM MST Walter Bright via Digitalmars- d wrote:I absolutely agree with all your points. From a compiler developer view, Walter is right. But from an application developer view, reducing the usability of Exceptions is terrible bad. Kind regards Andre[...]I would really hope not. IMHO, they are usually by far the best way to handle error conditions in a program. Anything else results in you having to deal with the various possible error conditions when the function returns, which is _far_ more unwieldy and much more error-prone. It also doesn't work well with function call chaining. [...]
Jan 09 2020
On Thursday, 9 January 2020 at 10:32:25 UTC, Jonathan M Davis wrote:I wouldn't mind us getting rid of Error in favor of killing the program on the spot, since that's actually better for debugging, and it avoids the whole argument about whether it's okay to catch Errors.Yes please!However, Exceptions are for runtime conditions that aren't necessarily a bug, and they can often be recovered from. So, being able to throw Exceptions is extremely useful, and the fact that they don't force you to check for error conditions everyewhere and instead just handle them in the spots where it makes sense to handle them makes the code _way_ cleaner. IMHO, having nothrow be the default would be terrible, and trying to treat exceptions as a legacy feature would be even worse. - Jonathan M Davis+1, and I would add that when writing modern C++11 with only linear types, exceptions + RAII quickly appear as the _only way_ to have correct desctruction teardown in error conditions (the least tested pathes). Anything else is just bring undetected errors in error paths, like in C. GC only catches non-reclaimed memory...
Jan 09 2020
On Thursday, 9 January 2020 at 10:32:25 UTC, Jonathan M Davis wrote:On Wednesday, January 8, 2020 11:26:41 PM MST Walter Bright via Digitalmars- d wrote:I doubt Stroustrup will let that happen. Highly unlikely scenario. Not sure why anyone would think that exceptions are gone just because some want to enable compilers configured to compile without exceptions to use more library containers (like std::vector)? C++17 certainly introduced new exceptions (std::filesystem::filesystem_error), and I believe more C++ setups enable exceptions today than a decade ago. For good reasons. No need to avoid exceptions in the whole program just because you don't want them in som specific locations (like render code). Whole program analysis and inter-procedural analysis should be able to take care of it.I'm aware that C++ is moving away from exceptions. I've been unhappy with exceptions for some time now (DMD doesn't use them for performance reasons), and C++ has evidently come to the same conclusion. I expect that exceptions will soon become a legacy feature.I would really hope not. IMHO, they are usually by far the best way to handle error conditions in a program. Anything else results in you having to deal with the various possible error conditions when the function returns, which is _far_ more unwieldy and much more error-prone. It also doesn't work well with function call chaining.
Jan 09 2020
On Thursday, 9 January 2020 at 13:26:36 UTC, Ola Fosheim Grøstad wrote:On Thursday, 9 January 2020 at 10:32:25 UTC, Jonathan M Davis wrote:Unfortunately Stroustrup only has its own vote and lobbying in what concerns C++'s future, hence his set of advocacy papers regarding C++'s roadmap. As posted in another comment, C++/WinRT and Android NDK are two such C++ setups.On Wednesday, January 8, 2020 11:26:41 PM MST Walter Bright via Digitalmars- d wrote:I doubt Stroustrup will let that happen. Highly unlikely scenario. Not sure why anyone would think that exceptions are gone just because some want to enable compilers configured to compile without exceptions to use more library containers (like std::vector)? C++17 certainly introduced new exceptions (std::filesystem::filesystem_error), and I believe more C++ setups enable exceptions today than a decade ago. For good reasons. No need to avoid exceptions in the whole program just because you don't want them in som specific locations (like render code). Whole program analysis and inter-procedural analysis should be able to take care of it.I'm aware that C++ is moving away from exceptions. I've been unhappy with exceptions for some time now (DMD doesn't use them for performance reasons), and C++ has evidently come to the same conclusion. I expect that exceptions will soon become a legacy feature.I would really hope not. IMHO, they are usually by far the best way to handle error conditions in a program. Anything else results in you having to deal with the various possible error conditions when the function returns, which is _far_ more unwieldy and much more error-prone. It also doesn't work well with function call chaining.
Jan 09 2020
On Thursday, 9 January 2020 at 13:45:44 UTC, Paulo Pinto wrote:As posted in another comment, C++/WinRT and Android NDK are two such C++ setups.I don't use NDK, but the documentation states clearly that it only supports new and delete for historic reasons: «C++ exceptions are supported by libc++, but they are disabled by default in ndk-build. This is because historically C++ exceptions were not available in the NDK. CMake and standalone toolchains have C++ exceptions enabled by default.» https://developer.android.com/ndk/guides/cpp-support
Jan 09 2020
On Thursday, 9 January 2020 at 14:16:38 UTC, Ola Fosheim Grøstad wrote:On Thursday, 9 January 2020 at 13:45:44 UTC, Paulo Pinto wrote:Ah, the Android documentation, where Medium posts by Google employees and twitter posts, and back in the day G+ posts, are more up to date than the actual documentation. https://android.googlesource.com/platform/ndk/+/ndk-r14-release/CHANGELOG.md "RTTI and exceptions are now on by default. This was done for improved compatibility with existing CMake projects. See https://github.com/android-ndk/ndk/issues/212."As posted in another comment, C++/WinRT and Android NDK are two such C++ setups.I don't use NDK, but the documentation states clearly that it only supports new and delete for historic reasons: «C++ exceptions are supported by libc++, but they are disabled by default in ndk-build. This is because historically C++ exceptions were not available in the NDK. CMake and standalone toolchains have C++ exceptions enabled by default.» https://developer.android.com/ndk/guides/cpp-support
Jan 09 2020
On Thursday, 9 January 2020 at 16:19:19 UTC, Paulo Pinto wrote:Ah, the Android documentation, where Medium posts by Google employees and twitter posts, and back in the day G+ posts, are more up to date than the actual documentation.But the change log does not contradict the documentation... It says that exceptions/STL are not shipped in the system runtime, but has to be embedded in the app bundle. Anyway, it does not matter. The point was only that it was for historic reasons, and that supporting exceptions are part of the available platform.
Jan 09 2020
On Thursday, 9 January 2020 at 10:32:25 UTC, Jonathan M Davis wrote:IMHO, having nothrow be the default would be terrible, and trying to treat exceptions as a legacy feature would be even worse.+1 Huge mistake in terms of programmer convenience. I don't really care what Rust or other languages are doing. I'm using D right now for a reason.
Jan 09 2020
On 09.01.20 11:32, Jonathan M Davis wrote:I wouldn't mind us getting rid of Error in favor of killing the program on the spot, since that's actually better for debugging,It really depends. There are situations where it may be better, but if you are trying to debug a non-deterministic condition that happens very rarely on someone else's machine exclusively in release builds, depending on a lot of user input, druntime shutting down the code that is supposed to collect enough information for you to have a chance to figure out what is going on in detail can hardly be described as "better". It should be possible to hook the kill function at least.
Jan 09 2020
On 1/9/2020 2:10 PM, Timon Gehr wrote:On 09.01.20 11:32, Jonathan M Davis wrote:Hooking the kill function is the way to do that - much better than exception unwinding.I wouldn't mind us getting rid of Error in favor of killing the program on the spot, since that's actually better for debugging,It really depends. There are situations where it may be better, but if you are trying to debug a non-deterministic condition that happens very rarely on someone else's machine exclusively in release builds, depending on a lot of user input, druntime shutting down the code that is supposed to collect enough information for you to have a chance to figure out what is going on in detail can hardly be described as "better". It should be possible to hook the kill function at least.
Jan 09 2020
On 10.01.20 02:28, Walter Bright wrote:On 1/9/2020 2:10 PM, Timon Gehr wrote:Exception unwinding gives you a stack trace.On 09.01.20 11:32, Jonathan M Davis wrote:Hooking the kill function is the way to do that - much better than exception unwinding.I wouldn't mind us getting rid of Error in favor of killing the program on the spot, since that's actually better for debugging,It really depends. There are situations where it may be better, but if you are trying to debug a non-deterministic condition that happens very rarely on someone else's machine exclusively in release builds, depending on a lot of user input, druntime shutting down the code that is supposed to collect enough information for you to have a chance to figure out what is going on in detail can hardly be described as "better". It should be possible to hook the kill function at least.
Jan 09 2020
On Thursday, January 9, 2020 8:25:17 PM MST Timon Gehr via Digitalmars-d wrote:On 10.01.20 02:28, Walter Bright wrote:A core dump at the point of the error gives you that and more. - Jonathan M DavisOn 1/9/2020 2:10 PM, Timon Gehr wrote:Exception unwinding gives you a stack trace.On 09.01.20 11:32, Jonathan M Davis wrote:Hooking the kill function is the way to do that - much better than exception unwinding.I wouldn't mind us getting rid of Error in favor of killing the program on the spot, since that's actually better for debugging,It really depends. There are situations where it may be better, but if you are trying to debug a non-deterministic condition that happens very rarely on someone else's machine exclusively in release builds, depending on a lot of user input, druntime shutting down the code that is supposed to collect enough information for you to have a chance to figure out what is going on in detail can hardly be described as "better". It should be possible to hook the kill function at least.
Jan 09 2020
On Friday, 10 January 2020 at 06:19:01 UTC, Jonathan M Davis wrote:A core dump at the point of the error gives you that and more. - Jonathan M DavisIf only recovering core dumps was reliable these days! It has become fashion to misconfigure systems to forward core dumps to systemd-journald where they are promptly discarded because the daemon considers the dumps to be too large for hiding them in the binary syslog. Apart from that, a crash handler that somehow logs or displays a backtrace is preferable when a crash happened on a user machine. Depending on the nature of the application, a core dump might contain sensitive information that cannot be legally shared with the developer (e.g. an in-memory copy of customer addresses).
Jan 10 2020
On 1/9/2020 7:25 PM, Timon Gehr wrote:Exception unwinding gives you a stack trace.Stack trace printing can be independent of the exception unwinding system. And it doesn't require all the complex tracking of RAII objects and their destructors, nor does it require executing code in the upstack functions.
Jan 09 2020
On Friday, 10 January 2020 at 01:28:35 UTC, Walter Bright wrote:On 1/9/2020 2:10 PM, Timon Gehr wrote:Sounds like a good idea, looking forward to that DIP. I would consider `nothrow` by default, like others, as seriously crippling to the language. However improving the efficiency, or changing the scheme, sounds like it would achieve everyone's goal. To give an example of a real world issue we have: - A server handling connections - We call `formattedWrite` with a `nothrow` sink and `nothrow` arguments (`toString` & co) - `formattedWrite` is not `nothrow` Why ? Because `std.format` is *full* of `enforce` calls to check the format string. So what should we do with them ? We *could* consider it user error (because the programmer didn't provide the proper format string) and just bulk-replace it with `Error`. But now, something as trivial as a wrong format string will crash my server, and for an error which is most likely recoverable (which is I guess the original reasoning behind the `enforce` calls). And such error are common, and can be in paths that are almost never triggered (e.g. an error message when the filesystem is full). So all things considered, having a non-`nothrow` `std.format` seems like the best compromise until someone comes up with a smart way to solve this without another downside. The same reasoning apply to many, many places, and that's why `nothrow` by default wouldn't make sense to me. Note at the moment, you can turn a non-`nothrow` function into a `nothrow` function with a single line of code: ``` void myFunc() nothrow { scope (failure) assert(0); // This makes the function `nothrow` formattedWrite(...); } ``` So in D, you can have non-`nothrow` function and a backtrace via exceptions, or `nothrow` function and a backtrace (abort in release mode). I think that we have it pretty good. P.S: https://issues.dlang.org/show_bug.cgi?id=20487On 09.01.20 11:32, Jonathan M Davis wrote:Hooking the kill function is the way to do that - much better than exception unwinding.I wouldn't mind us getting rid of Error in favor of killing the program on the spot, since that's actually better for debugging,It really depends. There are situations where it may be better, but if you are trying to debug a non-deterministic condition that happens very rarely on someone else's machine exclusively in release builds, depending on a lot of user input, druntime shutting down the code that is supposed to collect enough information for you to have a chance to figure out what is going on in detail can hardly be described as "better". It should be possible to hook the kill function at least.
Jan 10 2020
On Thursday, 9 January 2020 at 06:26:41 UTC, Walter Bright wrote:I expect that exceptions will soon become a legacy feature.I've seen no evidence of that whatsoever. Exceptions, like GC, are a proven winner in the real world with the vast majority of actually existing industry code using them successfully. Reminder that exceptions were invented because error codes *cannot* say the same thing. There are some new techniques being tried out in some places to varying levels of success. Those features are useful for other purposes too, so I wouldn't mind D picking some of them up, like I said in a previous message. But even with those available, I expect that error handling mode will remain relatively niche because of the enormous success exceptions have and have had proven in industry.
Jan 09 2020
On 1/9/20 7:51 AM, Adam D. Ruppe wrote:On Thursday, 9 January 2020 at 06:26:41 UTC, Walter Bright wrote:I could see "nothrow by default" added making it an easier transition to Swift-like error handling (errors passed back on the stack, made to look like exceptions to user code). I would like to see dip1008 get some more TLC before we think about abandoning exceptions altogether. And switching Errors to non throwables, terminate in place with stack trace, would be very useful. -SteveI expect that exceptions will soon become a legacy feature.I've seen no evidence of that whatsoever. Exceptions, like GC, are a proven winner in the real world with the vast majority of actually existing industry code using them successfully. Reminder that exceptions were invented because error codes *cannot* say the same thing. There are some new techniques being tried out in some places to varying levels of success. Those features are useful for other purposes too, so I wouldn't mind D picking some of them up, like I said in a previous message. But even with those available, I expect that error handling mode will remain relatively niche because of the enormous success exceptions have and have had proven in industry.
Jan 09 2020
On Thu, Jan 09, 2020 at 10:07:53AM -0500, Steven Schveighoffer via Digitalmars-d wrote:On 1/9/20 7:51 AM, Adam D. Ruppe wrote:+1. This anti-exception sentiment I have a hard time understanding. [...]On Thursday, 9 January 2020 at 06:26:41 UTC, Walter Bright wrote:I expect that exceptions will soon become a legacy feature.I've seen no evidence of that whatsoever. Exceptions, like GC, are a proven winner in the real world with the vast majority of actually existing industry code using them successfully.I could see "nothrow by default" added making it an easier transition to Swift-like error handling (errors passed back on the stack, made to look like exceptions to user code).[...] Now *this* I can stand by. Which makes me wonder if what's bothering Walter about exceptions is not so much the concept of exceptions itself, but their current implementation via stack unwinding, which requires expensive setup of stack frames and what-not. But it's possible to implement it differently, while still presenting an exception-like interface to user code. You could pass errors back on the stack, or even via a register dedicated for indicating error conditions, and have the compiler automatically generate code for branching to an error-handling part of the function (or have user code explicitly test for it, if they so wish). This can be transparent to user code. T -- Who told you to swim in Crocodile Lake without life insurance??
Jan 09 2020
Am Thu, 09 Jan 2020 08:02:16 -0800 schrieb H. S. Teoh:On Thu, Jan 09, 2020 at 10:07:53AM -0500, Steven Schveighoffer via Digitalmars-d wrote:I think everyone who wants to move away from exceptions really wants to move away from two things: 1) Stack unwinding and the overhead it involves (memory, implementation complexity, ...) 2) (Forced) dynamic allocation of error state. How often do you actually use more than 1 word of error state? That's basically what Sutter proposes for C++, what I summarized in https://forum.dlang.org/post/qusdvn$635$1 digitalmars.com and what you quickly summarized in your idea. AFAICS nobody (few people?) wants to get rid of the high-level try-catch exception semantics. However, some might not want to catch by runtime type but implement the catch matching using some kind of error codes. The only thing you loose here is having complex exception type trees and catching base classes. Also as I explained in the linked post, try-catch is fine if you handle multiple error sources a once. If you instead have to handle errors for each function call individually, a more fine grain solution than try { auto foo = someFunction(); try { foo = someFunction2(foo); } catch(Exception3 e) ... catch(Exception4 e) } catch(Exception e) ... catch(Exception2 e) would be useful in addition. -- JohannesOn 1/9/20 7:51 AM, Adam D. Ruppe wrote:+1. This anti-exception sentiment I have a hard time understanding. [...]On Thursday, 9 January 2020 at 06:26:41 UTC, Walter Bright wrote:I expect that exceptions will soon become a legacy feature.I've seen no evidence of that whatsoever. Exceptions, like GC, are a proven winner in the real world with the vast majority of actually existing industry code using them successfully.I could see "nothrow by default" added making it an easier transition to Swift-like error handling (errors passed back on the stack, made to look like exceptions to user code).[...] Now *this* I can stand by. Which makes me wonder if what's bothering Walter about exceptions is not so much the concept of exceptions itself, but their current implementation via stack unwinding, which requires expensive setup of stack frames and what-not. But it's possible to implement it differently, while still presenting an exception-like interface to user code. You could pass errors back on the stack, or even via a register dedicated for indicating error conditions, and have the compiler automatically generate code for branching to an error-handling part of the function (or have user code explicitly test for it, if they so wish). This can be transparent to user code. T
Jan 09 2020
On Thu, Jan 09, 2020 at 07:05:24PM -0000, Johannes Pfau via Digitalmars-d wrote:Am Thu, 09 Jan 2020 08:02:16 -0800 schrieb H. S. Teoh:[...]I think everyone who wants to move away from exceptions really wants to move away from two things: 1) Stack unwinding and the overhead it involves (memory, implementation complexity, ...) 2) (Forced) dynamic allocation of error state. How often do you actually use more than 1 word of error state?What can you practically throw, though, if you only have 1 word of error state? I suppose you could store a pointer to some exception object in there, but then that brings us back to issue (2). Unless you have some kind of thread-local global for storing exception objects?That's basically what Sutter proposes for C++, what I summarized in https://forum.dlang.org/post/qusdvn$635$1 digitalmars.com and what you quickly summarized in your idea. AFAICS nobody (few people?) wants to get rid of the high-level try-catch exception semantics.[...] That's also my suspicion. It's not really the exceptions themselves that people object to, but the associated implementation issues. T -- Just because you can, doesn't mean you should.
Jan 09 2020
Am Thu, 09 Jan 2020 12:39:57 -0800 schrieb H. S. Teoh:On Thu, Jan 09, 2020 at 07:05:24PM -0000, Johannes Pfau via Digitalmars-d wrote:As far as I understood Sutter's proposal it's one word of 'context'. But a function actually returns two words (i.e. two words of state), where one is the error category and the second is the context. Essentially std::error_code. In practice the category code is a pointer to a dummy variable in static data. So for example you could do this: module filesystem; ubyte FileSystemErrorClass; enum FileSystemError { doesNotExist, PermissionDenied } return Error(&FileSystemErrorClass, FileSystemError.doesNotExist); if (error.class == &FileSystemErrorClass) { auto kind = cast(FileSystemError)error.context; } The ErrorClass is really the Tag, the context is then arbitrary (Tag- specific) data. I.e. you could reference TLS data, but you could just as well allocate data manually and store a pointer to heap memory in the context. Obviously users would not write such low level code, that's just the low level ABI. -- JohannesAm Thu, 09 Jan 2020 08:02:16 -0800 schrieb H. S. Teoh:[...]I think everyone who wants to move away from exceptions really wants to move away from two things: 1) Stack unwinding and the overhead it involves (memory, implementation complexity, ...) 2) (Forced) dynamic allocation of error state. How often do you actually use more than 1 word of error state?What can you practically throw, though, if you only have 1 word of error state? I suppose you could store a pointer to some exception object in there, but then that brings us back to issue (2). Unless you have some kind of thread-local global for storing exception objects?
Jan 09 2020
On Thursday, 9 January 2020 at 21:29:00 UTC, Johannes Pfau wrote:As far as I understood Sutter's proposal it's one word of 'context'. But a function actually returns two words (i.e. two words of state), where one is the error category and the secondWell, on x86 a register can be 256 bits or more... But AFAIK C++ implementations do not allocate memory for exceptions dynamically. So you only pay the price of the constructor and potential cache miss.
Jan 09 2020
Am Thu, 09 Jan 2020 21:35:51 +0000 schrieb Ola Fosheim Grøstad:On Thursday, 9 January 2020 at 21:29:00 UTC, Johannes Pfau wrote:I don't know the details, but I guess that's the throw by value / catch by reference idiom in C++. Not sure how it's actually implemented. In addition to constructor + cache miss you'll also have to do RTTI to match the object to the types in the catch handlers (and for the downcast). -- JohannesAs far as I understood Sutter's proposal it's one word of 'context'. But a function actually returns two words (i.e. two words of state), where one is the error category and the secondWell, on x86 a register can be 256 bits or more... But AFAIK C++ implementations do not allocate memory for exceptions dynamically. So you only pay the price of the constructor and potential cache miss.
Jan 09 2020
On Thursday, 9 January 2020 at 22:39:25 UTC, Johannes Pfau wrote:I don't know the details, but I guess that's the throw by value / catch by reference idiom in C++. Not sure how it's actually implemented.It is implementation defined... in clang it uses a dedicated allocator function. But as you can see it does some optimizations: https://llvm.org/docs/ExceptionHandling.htmlIn addition to constructor + cache miss you'll also have to do RTTI to match the object to the types in the catch handlers (and for the downcast).I assume that is how clang++ does it, but you don't _have_ to use RTTI structures. You could do it other ways. But in C++ exceptions tend not to be used on the fast-path (meaning, used primarily for unexpected outcomes).
Jan 09 2020
On 1/9/2020 12:39 PM, H. S. Teoh wrote:It's not really the exceptions themselves that people object to, but the associated implementation issues.Yes. They're very costly, even in code that never throws. An approach I've been using with modest success is to design errors entirely out of the code. For example, in dmd a lot of errors are handled by making a special AST node for "Error". Subsequent code does nothing with Error nodes. (Analogously to how floating point code deals with errors, it just sets a NaN value, which is sticky.) Another technique is to check for errors in the data first, then the processing code does not have to check, and cannot fail. I enjoy trying to set up an API so it cannot fail, then no special code is needed for errors. Of course, this isn't always possible, and isn't a general solution. But it's nice when one can make it work. P.S. I hate throwing constructors, and would force them to be nothrow in D if I weren't faced with a tsunami of objection to it :-)
Jan 09 2020
On Friday, 10 January 2020 at 01:40:00 UTC, Walter Bright wrote:On 1/9/2020 12:39 PM, H. S. Teoh wrote:The longer this discussion drags on, the more I doubt my understanding of this topic. What exactly is the execution overhead for non-throwing code paths? I believe I understand the overhead once stack unwinding needs to be performed, but how is code generation affected for the normal path?It's not really the exceptions themselves that people object to, but the associated implementation issues.Yes. They're very costly, even in code that never throws.
Jan 09 2020
On 1/9/2020 7:42 PM, Gregor Mückl wrote:What exactly is the execution overhead for non-throwing code paths? I believe I understand the overhead once stack unwinding needs to be performed, but how is code generation affected for the normal path?You need a mechanism for jumping to the scope guard code. Can't do any code motion optimizations across the boundaries of such code. Can't do register assignments for variables that are "live" across that boundary. Just write some code with destructors in C++, compile it with and without exceptions enabled, and dump the generated assembler.
Jan 09 2020
On Friday, 10 January 2020 at 07:06:31 UTC, Walter Bright wrote:On 1/9/2020 7:42 PM, Gregor Mückl wrote:I played around with this as you suggested. The most concise example that I could find where -fno-exceptions makes a difference is this: https://godbolt.org/z/WxnsOm The version with exceptions reserves a register for something at the start of the function (rbp in this case), but I can't tell what it's for. It is used in the unwinding handler that is appended to the function after the ret instruction.What exactly is the execution overhead for non-throwing code paths? I believe I understand the overhead once stack unwinding needs to be performed, but how is code generation affected for the normal path?You need a mechanism for jumping to the scope guard code. Can't do any code motion optimizations across the boundaries of such code. Can't do register assignments for variables that are "live" across that boundary. Just write some code with destructors in C++, compile it with and without exceptions enabled, and dump the generated assembler.
Jan 10 2020
On Friday, 10 January 2020 at 03:42:53 UTC, Gregor Mückl wrote:The longer this discussion drags on, the more I doubt my understanding of this topic. What exactly is the execution overhead for non-throwing code paths?My understanding is that there is no overhead for functions that transitively don't throw or catch. It's also my experience that A. _the bottlnecksin a program are almost NEVER functions that throw_ so why optimize the other parts? B. Giving up a little bit of safety for performance ruins all potential gains because of the overhead of introducing more bugs. Fixing one such bug will negate whatever performance gain. C. If you do need performance AND errors (typically: parsing without ranges/exceptions vs parsing with error codes) then you can resort locally to error codes. D. Error codes makes very easy to forget them, misuse them, and makes code less readable. They make very easy to conflate unrecoverable errors and recoverable errors. The current internet backlash against exceptions is really puzzling to me: it's a sort of _revisionism_ because in the trenches exceptions are holding things together so they don't break more horribly. It think these people don't know how worse it could be.
Jan 10 2020
On Friday, 10 January 2020 at 01:40:00 UTC, Walter Bright wrote:On 1/9/2020 12:39 PM, H. S. Teoh wrote:Well said. Another option would be functional programming, with an explicit either type. But it is not for everyone. I have found that I don't need exceptions a lot, and when I do, it is almost always because I want to take advantage of the fact that they add implicit returns up call stack until the nearest catch. Then they come in real handy, especially because they are transparent to the intermediate functions. Of course, that is exactly why they are costly. They works great with IO, but I tend to avoid them for everything else.It's not really the exceptions themselves that people object to, but the associated implementation issues.Yes. They're very costly, even in code that never throws. An approach I've been using with modest success is to design errors entirely out of the code. For example, in dmd a lot of errors are handled by making a special AST node for "Error". Subsequent code does nothing with Error nodes. (Analogously to how floating point code deals with errors, it just sets a NaN value, which is sticky.) Another technique is to check for errors in the data first, then the processing code does not have to check, and cannot fail. I enjoy trying to set up an API so it cannot fail, then no special code is needed for errors. Of course, this isn't always possible, and isn't a general solution. But it's nice when one can make it work. P.S. I hate throwing constructors, and would force them to be nothrow in D if I weren't faced with a tsunami of objection to it :-)
Jan 10 2020
On Thursday, 9 January 2020 at 06:26:41 UTC, Walter Bright wrote:I'm aware that C++ is moving away from exceptions. I've been unhappy with exceptions for some time now (DMD doesn't use them for performance reasons), and C++ has evidently come to the same conclusion. I expect that exceptions will soon become a legacy feature.Value type exceptions as proposed by Herb and others are still exceptions. Error codes are just to keep the "exceptions only over by dead body" crowd happy, and to try to move them away from non-compliant C++ with exceptions and RTTI turned off. C++/WinRT turns COM error codes into C++ exceptions, Android NDK has C++ exceptions enabled by default.
Jan 09 2020
On Thursday, 9 January 2020 at 06:26:41 UTC, Walter Bright wrote:I expect that exceptions will soon become a legacy feature.Honestly, to me this sounds like "I don't like/use exceptions therefore get rid of them (not as a feature, but just stop using it anywhere or make them too annoying to bother with)", which is understandable since you mostly seem to work on projects that require high-performance (like DMD), but not every project is unable to afford the overhead of exceptions. In terms of making `nothrow` the default, I dread to think of how annoying it'd be for a commonly used function in a codebase to not be `nothrow`, then having to throw in the `throw` keyword into the ever-growing attribute soup for any function that calls it. While at the moment if a function is `nothrow`, then it's optional for a caller to be `nothrow` so it doesn't stick in my face as much. I guess the same argument could be applied to safe by default, but having to deal with an ` system` function is likely much rarer and confined than having to deal with a more widely used `throw` function, making it more manageable, while ` safe` also provide heavy benefits outside of just "performance". I fear this will also discourage the use of exceptions, and push people to overuse error codes which don't have any standard pattern; aren't able to provide additional info beyond the actual error itself (e.g. can't provide certain info or values that caused the error); require the caller to produce any error messages (which might mean functions specifically designed for other functions' error codes); and more annoyingly either a type that returns both value and error (which isn't too bad, but a bit annoying), or requires the caller to pass an out/ref parameter(s) in case of an error (again, very annoying to deal with). I don't know. It just feels like this proposal will add more friction with using D in the general case, as a compromise for giving extra performance in the niche case. I'm a bit of a dimwit, so apologies if I've misunderstood how this'd end up working, but my current view is that it'd make my own experience with D more painful for unequal amount of benefit.
Jan 09 2020
On Saturday, 4 January 2020 at 21:38:53 UTC, Walter Bright wrote:The first step is to add `throw` as a function attribute, https://github.com/dlang/DIPs/pull/167 The next step will be to make nothrow the default. I have not prepared a DIP for that yet, but will. The short rationale is that exceptions being a "pay only if you use them" is a complete fraud. They're expensive to support, meaning performance programs use other ways of signalling errors and use nothrow.Do you have a plan for standardizing an alternative recoverable-error-signalling method? For example `Result` types? Or something else?
Jan 13 2020
On 05/01/2020 5:05 AM, Steven Schveighoffer wrote:Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda? -SteveWell we could use throw, or add throws like in Java. But after Java I do think we have the right defaults for exceptions in general. The Java way forces you to use the IDE to handle that stuff, instead of yourself. Which would be a major loss for D.
Jan 04 2020
On Saturday, 4 January 2020 at 16:05:10 UTC, Steven Schveighoffer wrote:We we going to get a new keyword/uda?this should be done regardless of defaults! for ALL these attributes. We should do that and perhaps figure out it going into scopes too. nothrow: struct A { void foo() throws {} // the nothrow could be inherited from outside if it is overridable inside. } nothrow by default btw I'm.... concerned about. but if we fixed the anti-attributes and descending into scopes it is far more manageable regardless of default.
Jan 04 2020
On Saturday, January 4, 2020 9:05:10 AM MST Steven Schveighoffer via Digitalmars-d wrote:Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda?We really should add a way to negate all of the attributes regardless of the defaults, but nothrow by default would be terrible IMHO. Aside from really performance-sensitive code, exceptions are usually the best and cleanest way to deal with error conditions that aren't bugs. If nothrow were the default, either we'd have to slap whatever the equivalent of !nothrow would be all over the place, or exceptions would be seriously compromised as a means of error handling. - Jonathan M Davis
Jan 04 2020
On Saturday, 4 January 2020 at 16:05:10 UTC, Steven Schveighoffer wrote:What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda?Walter's current plan seems to be allowing the `throw` keyword to be used as an attribute. https://github.com/dlang/DIPs/pull/167/files That still leaves the question how nogc and pure will be negated, and how to specify the default attributes for a module without affecting template functions and functions with `auto` return-type.
Jan 04 2020
On Saturday, 4 January 2020 at 20:53:34 UTC, Dennis wrote:Walter's current plan seems to be allowing the `throw` keyword to be used as an attribute. https://github.com/dlang/DIPs/pull/167/filesthat looks good to me btw. I'm against the default change but that DIP appears to do what I'd want - the negating behavior is also well done.That still leaves the question how nogc and pure will be negated, and how to specify the default attributes for a module without affecting template functions and functions with `auto` return-type.lol we could always use existing keywords like crazy: pure!false nogc!true nothrow!default virtual!false or something similar. gotta love the double negatives but meh. throw already being a keyword is convenient. pure is already positive so convenient. just nogc, the odd one out. but like regardless i wouldn't oppose such a thing. i also wouldn't oppose impure and gc keywords added. I also wouldn't oppose templates simply not being affected by scope-level keyword attributes. That might be a winner actually.... maybe. If you want it to apply to the template, you'd just have to explicitly write it again. They get special case because of the inference rule. Could also mean safe class Foo { final void my_template()(); // actually inferred, despite outer safe } idk, just throwing out ideas. To be honest I'd take just about *anything* that actually happens now over some other idea that we have to wait several years for again.
Jan 04 2020
On Saturday, 4 January 2020 at 21:07:03 UTC, Adam D. Ruppe wrote:lol we could always use existing keywords like crazy: pure!false nogc!true nothrow!default virtual!false or something similar. gotta love the double negatives but meh. throw already being a keyword is convenient. pure is already positive so convenient. just nogc, the odd one out.So do you allow `nothrow!false` as well? Or: - nothrow!(false) - nothrow!0 - nothrow!(0 || false) - throw!false - throw!true Also, which one gets put in the tuple when calling: __traits(getFunctionAttributes, func); Currently " system", " trusted" or " safe" is always there, while "nothrow", "pure" and " nogc" are either there or not. What is the canonical way to check that a function does not have the "nothrow" attribute? When there's multiple options, it's bound to be confusing. It might be bikeshedding over the real issue (which is that it is still impossible to negate these attributes at all), but considering attribute soup is already a common complaint, I don't want it to become worse.
Jan 04 2020
On Saturday, 4 January 2020 at 21:07:03 UTC, Adam D. Ruppe wrote:[...] lol we could always use existing keywords like crazy: pure!false nogc!true nothrow!default virtual!false or something similar.I had this idea too (for my toy language). One potential problem is safe/ system/ trusted though, but you could just keep trusted as a separate attribute.[...] I also wouldn't oppose templates simply not being affected by scope-level keyword attributes. That might be a winner actually.... maybe. If you want it to apply to the template, you'd just have to explicitly write it again. They get special case because of the inference rule.This is an interesting idea. How often do you actually want your templates to be affected by scope-level attributes?
Jan 04 2020
On 1/4/2020 1:07 PM, Adam D. Ruppe wrote:On Saturday, 4 January 2020 at 20:53:34 UTC, Dennis wrote:Your comment is exactly why I'm doing this as two DIPs instead of one :-)Walter's current plan seems to be allowing the `throw` keyword to be used as an attribute. https://github.com/dlang/DIPs/pull/167/filesthat looks good to me btw. I'm against the default change but that DIP appears to do what I'd want - the negating behavior is also well done.
Jan 04 2020
On Saturday, 4 January 2020 at 21:07:03 UTC, Adam D. Ruppe wrote:On Saturday, 4 January 2020 at 20:53:34 UTC, Dennis wrote:D should make attribute keywords consistent by adding inversed value using "!". This will help to reduce the number of attributes and a way to inverse the block setting. For any attributes that support block in form of ...":" or ...{...}, it should apply to all functions & function/delegate alias. trusted is a dangerous construct and should not be allowed to be use in block construct. pure !pure gc !gc => as current nogc and depreciate nogc throw !throw => as current nothrow and depreciate nothrow safe !safe => as current system and depreciate systemWalter's current plan seems to be allowing the `throw` keyword to be used as an attribute. https://github.com/dlang/DIPs/pull/167/filesthat looks good to me btw. I'm against the default change but that DIP appears to do what I'd want - the negating behavior is also well done.That still leaves the question how nogc and pure will be negated, and how to specify the default attributes for a module without affecting template functions and functions with `auto` return-type.lol we could always use existing keywords like crazy: pure!false nogc!true nothrow!default virtual!false or something similar. gotta love the double negatives but meh. throw already being a keyword is convenient. pure is already positive so convenient. just nogc, the odd one out. but like regardless i wouldn't oppose such a thing. i also wouldn't oppose impure and gc keywords added. I also wouldn't oppose templates simply not being affected by scope-level keyword attributes. That might be a winner actually.... maybe. If you want it to apply to the template, you'd just have to explicitly write it again. They get special case because of the inference rule. Could also mean safe class Foo { final void my_template()(); // actually inferred, despite outer safe } idk, just throwing out ideas. To be honest I'd take just about *anything* that actually happens now over some other idea that we have to wait several years for again.
Jan 04 2020
On Sunday, 5 January 2020 at 04:37:57 UTC, apz28 wrote:pure !pure gc !gc => as current nogc and depreciate nogc throw !throw => as current nothrow and depreciate nothrow safe !safe => as current system and depreciate systemI would like to have everything with : nothrow, pure, nopure (or ! throw, ! pure but I like nothrow and nopure more). And one my even think about const to avoid the ambiguity of "const int f()".
Jan 05 2020
On Saturday, 4 January 2020 at 21:07:03 UTC, Adam D. Ruppe wrote:pure!false nogc!true nothrow!default virtual!falsenopure / novirtual / no... #safe #nothrow #nopure myuda void blah() { }
Jan 07 2020
On Saturday, 4 January 2020 at 16:05:10 UTC, Steven Schveighoffer wrote:Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda? -SteveWHY would nothrow by the default in the first place? Exceptions are about the best error handling mechanism and there is no better altnernative in D. There is a rationale for making people use a safe subset (supposed to be a good thing) but what is the rationale for gently moving them to _avoid exceptions?_ The performance point is a bit strange since people that want speed without correctness doesn't neither correctness, nor speed. The solution is to make nothrow whatever is slow and will be in benchmarks... Textbook premature optimization at the expense of correctness.
Jan 05 2020
On Monday, 6 January 2020 at 03:13:09 UTC, Guillaume Piolat wrote:On Saturday, 4 January 2020 at 16:05:10 UTC, Steven Schveighoffer wrote:`nothrow` by default doesn't stop you from using exceptions, it just forces you to either catch them or mark your function as throwing.Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda? -SteveWHY would nothrow by the default in the first place? Exceptions are about the best error handling mechanism and there is no better altnernative in D.
Jan 05 2020
On Monday, 6 January 2020 at 06:58:34 UTC, Paul Backus wrote:`nothrow` by default doesn't stop you from using exceptions, it just forces you to either catch them or mark your function as throwing.It kinda does: generic programming. Say if I want to do critical math and create a numeric class that throws on inaccuracies. Then I cannot use libraries that "forgot" to add "throws" because whenever I provide a lambda that use critical math those libraries will refuse to take them. Attributes like "nothrow" should be for public APIs. For most code is better to do this using semantic analysis.
Jan 06 2020
On Monday, 6 January 2020 at 06:58:34 UTC, Paul Backus wrote:On Monday, 6 January 2020 at 03:13:09 UTC, Guillaume Piolat wrote:What do you think people will do: A - handle the exception correctly at the right place and mark the whole chain of calls as `throws` B - mark a single function as `nothrow` and catch the Exception with a dummy handler B is dangerously easier to do. Also the statu quo isn't so bad: an unhandled exception crash, which is an unrecoverable error, as good as an assertion-in-release. Not handling one is a bug, and it acts like so. How many times will D break all code in the future?On Saturday, 4 January 2020 at 16:05:10 UTC, Steven Schveighoffer wrote:`nothrow` by default doesn't stop you from using exceptions, it just forces you to either catch them or mark your function as throwing.Just wanted to bring this up, and not muddy the other thread. What do you put if you throw? safe by default has alternatives. nothrow does not. Are we going to get a new keyword/uda? -SteveWHY would nothrow by the default in the first place? Exceptions are about the best error handling mechanism and there is no better altnernative in D.
Jan 06 2020