www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - nothrow by default

reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
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?

 -Steve
Seems like we'd have to. Or on/off attribute parameters.
Jan 04
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/4/2020 8:06 AM, Mike Parker wrote:
 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?

 -Steve
Seems like we'd have to. Or on/off attribute parameters.
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.
Jan 04
next sibling parent reply JN <666total wp.pl> writes:
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
next sibling parent IGotD- <nise nise.com> writes:
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:
 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.
I agree, we could use "throws" instead of "throw".
Jan 04
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/5/2020 10:57 AM, Jacob Carlborg wrote:
 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 ;)
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.
Jan 09
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/4/2020 3:06 PM, Adam D. Ruppe wrote:
 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.
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.)
 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
prev sibling next sibling parent reply Johannes Pfau <nospam example.com> writes:
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
next sibling parent reply Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
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
next sibling parent reply Johannes Pfau <nospam example.com> writes:
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.
 
 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.
This is actually similar to the way exception chaining is implemented in GDC right now. So that's probably not a hard problem.
 
 * 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
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
Jan 05
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
next sibling parent reply JN <666total wp.pl> writes:
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
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
prev sibling parent reply Johannes Pfau <nospam example.com> writes:
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:
 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...)
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.
 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
next sibling parent reply Johannes Pfau <nospam example.com> writes:
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:
 
 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...)
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/
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. -- Johannes
Jan 05
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/5/2020 4:27 AM, Gregor Mückl wrote:
 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.
For D, the `throw` attribute will apply to the function, not the statement.
Jan 08
prev sibling next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
prev sibling next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
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:
 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.
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.
 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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
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:
 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?
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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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 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.
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.
Jan 05
parent rikki cattermole <rikki cattermole.co.nz> writes:
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:
 On 06/01/2020 2:51 AM, Ola Fosheim Grøstad wrote:
 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.
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.
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.
Jan 05
prev sibling parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
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
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
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:
 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.
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++.
Jan 10
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
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 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.
My point is c++ is not removing what is being proposed for adding 'throw'
Jan 10
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
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
parent reply Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
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:
 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.
Throw was always the default, just like D.
Jan 11
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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:
 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.
Throw was always the default, just like D.
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 Davis
Jan 11
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
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 along
Not 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 a
Noexcept 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
parent Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
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:
 noexcept does the same thing as throw(), but throw() is going 
 away along
Not quite. In c++17 throw() was redefined to noexcept, but prior to this it should unwind the stack with unexpected as the exception.
The unexpected handler ( not a regular exception).
Jan 11
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
next sibling parent Andre Pany <andre s-e-a-p.de> writes:
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 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. [...]
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
Jan 09
prev sibling next sibling parent Guillaume Piolat <first.last gmail.com> writes:
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
prev sibling next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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'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.
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.
Jan 09
parent reply Paulo Pinto <pjmlp progtools.org> writes:
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:
 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.
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.
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.
Jan 09
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
parent reply Paulo Pinto <pjmlp progtools.org> writes:
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:
 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
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."
Jan 09
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
prev sibling next sibling parent bachmeier <no spam.net> writes:
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
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/9/2020 2:10 PM, Timon Gehr wrote:
 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.
Hooking the kill function is the way to do that - much better than exception unwinding.
Jan 09
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10.01.20 02:28, Walter Bright wrote:
 On 1/9/2020 2:10 PM, Timon Gehr wrote:
 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.
Hooking the kill function is the way to do that - much better than exception unwinding.
Exception unwinding gives you a stack trace.
Jan 09
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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:
 On 1/9/2020 2:10 PM, Timon Gehr wrote:
 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.
Hooking the kill function is the way to do that - much better than exception unwinding.
Exception unwinding gives you a stack trace.
A core dump at the point of the error gives you that and more. - Jonathan M Davis
Jan 09
parent Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
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 Davis
If 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
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent Mathias Lang <pro.mathias.lang gmail.com> writes:
On Friday, 10 January 2020 at 01:28:35 UTC, Walter Bright wrote:
 On 1/9/2020 2:10 PM, Timon Gehr wrote:
 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.
Hooking the kill function is the way to do that - much better than exception unwinding.
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=20487
Jan 10
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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 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.
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. -Steve
Jan 09
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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.
+1. This anti-exception sentiment I have a hard time understanding. [...]
 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
parent reply Johannes Pfau <nospam example.com> writes:
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:
 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 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.
+1. This anti-exception sentiment I have a hard time understanding. [...]
 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
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. -- Johannes
Jan 09
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
next sibling parent reply Johannes Pfau <nospam example.com> writes:
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:
 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?
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. -- Johannes
Jan 09
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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 second
Well, 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
parent reply Johannes Pfau <nospam example.com> writes:
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:
 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
Well, 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.
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). -- Johannes
Jan 09
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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.html
 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).
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
On Friday, 10 January 2020 at 01:40:00 UTC, Walter Bright wrote:
 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.
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?
Jan 09
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
On Friday, 10 January 2020 at 07:06:31 UTC, Walter Bright wrote:
 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.
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.
Jan 10
prev sibling parent Guillaume Piolat <first.last gmail.com> writes:
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
prev sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Friday, 10 January 2020 at 01:40:00 UTC, Walter Bright wrote:
 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 :-)
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.
Jan 10
prev sibling next sibling parent Paulo Pinto <pjmlp progtools.org> writes:
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
prev sibling parent SealabJaster <sealabjaster gmail.com> writes:
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
prev sibling parent Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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
prev sibling next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
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?
 
 -Steve
Well 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
prev sibling next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
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
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
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
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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/files
that 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
next sibling parent Dennis <dkorpel gmail.com> writes:
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
prev sibling next sibling parent Les De Ridder <les lesderid.net> writes:
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
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/4/2020 1:07 PM, Adam D. Ruppe wrote:
 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/files
that 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.
Your comment is exactly why I'm doing this as two DIPs instead of one :-)
Jan 04
prev sibling next sibling parent reply apz28 <home home.com> writes:
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:
 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 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.
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 system
Jan 04
parent berni44 <dlang d-ecke.de> writes:
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  system
I 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
prev sibling parent Andrea Fontana <nospam example.com> writes:
On Saturday, 4 January 2020 at 21:07:03 UTC, Adam D. Ruppe wrote:
 pure!false
  nogc!true
 nothrow!default
 virtual!false
nopure / novirtual / no... #safe #nothrow #nopure myuda void blah() { }
Jan 07
prev sibling parent reply Guillaume Piolat <first.last gmail.com> writes:
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?

 -Steve
WHY 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
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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
WHY 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.
`nothrow` by default doesn't stop you from using exceptions, it just forces you to either catch them or mark your function as throwing.
Jan 05
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
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
prev sibling parent Guillaume Piolat <first.last gmail.com> writes:
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:
 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?

 -Steve
WHY 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.
`nothrow` by default doesn't stop you from using exceptions, it just forces you to either catch them or mark your function as throwing.
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?
Jan 06