www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - "I told you so": noreturn sucks a leech and has virtually no utility

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
It has been predicted by several folks that noreturn has very limited 
utility and a variety of weird corner cases that will cause a hecatomb 
of complications in the language, the standard library, and the 
implementation.

(For starters: how the hell is "will not return" a type that's supposed 
to do all or at least most things that types do, when even the phrase 
defining it has no subject? It's about a FUNCTION that won't return, so 
it should be an attribute applicable to a FUNCTION!)

In spite of these voices of reason, noreturn proceeded with a definition 
and implementation as expected: execrable.

I was looking today through the wretched isAutodecodableString (how did 
we ever think /that/ was a good idea?) and found a number of weird 
occurrences of noreturn. Where? Of course, where you least expect or 
want it.

Thirty-seven `noreturn` occurrences in phobos.

Ten opened bugs.

This is literally why we can't have nice things. If all that investment 
went to literally any other proposal, it would be a net positive.
Oct 15 2021
next sibling parent Dennis <dkorpel gmail.com> writes:
On Friday, 15 October 2021 at 17:13:29 UTC, Andrei Alexandrescu 
wrote:
 It has been predicted by several folks that noreturn has very 
 limited utility and a variety of weird corner cases that will 
 cause a hecatomb of complications in the language, the standard 
 library, and the implementation.
Reference? I'm looking back at the DIP 1034 threads: [Review round 1 - Discussion](https://forum.dlang.org/post/ooofastmtzmuylnjesyl forum.dlang.org) [Review round 1 - Feedback](https://forum.dlang.org/post/arcpszmdarekxtnsnwfl forum.dlang.org) [Final review - Discussion](https://forum.dlang.org/post/heylgwkzcpfqmqytiezq forum.dlang.org) [Final review - Feedback](https://forum.dlang.org/post/ceicwtqcalmgiteudkjd forum.dlang.org) I don't see it there.
Oct 15 2021
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
Hm? Isn't it a backend feature? How it complicates the language?
Oct 15 2021
next sibling parent MoonlightSentinel <moonlightsentinel disroot.org> writes:
On Friday, 15 October 2021 at 18:28:37 UTC, Kagamin wrote:
 Hm? Isn't it a backend feature? How it complicates the language?
DIP 1034 proposed a bottom type named `noreturn`, not C++ `[[noreturn]]`.
Oct 15 2021
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Friday, 15 October 2021 at 18:28:37 UTC, Kagamin wrote:
 Hm? Isn't it a backend feature? How it complicates the language?
No, you can use it everywhere a Type can be used: cast to type, TemplateTypeParameter, VarDeclaration type, etc. And most of the time this is obviously not useful and has to be rejected, i.e during the semantic, i.e by the front-end. So far this is not enirely done, which creates cases of ICE in the backend.
Oct 15 2021
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/15/21 8:52 PM, Basile B. wrote:
 
 No, you can use it everywhere a Type can be used: cast to type, 
 TemplateTypeParameter, VarDeclaration type, etc. And most of the time 
 this is obviously not useful and has to be rejected, i.e during the 
 semantic, i.e by the front-end.
I don't think rejecting everything that's "obviously not useful" is the right approach. It just causes headaches for generic code.
Oct 15 2021
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 15 October 2021 at 18:59:55 UTC, Timon Gehr wrote:
 On 10/15/21 8:52 PM, Basile B. wrote:
 
 No, you can use it everywhere a Type can be used: cast to 
 type, TemplateTypeParameter, VarDeclaration type, etc. And 
 most of the time this is obviously not useful and has to be 
 rejected, i.e during the semantic, i.e by the front-end.
I don't think rejecting everything that's "obviously not useful" is the right approach. It just causes headaches for generic code.
Seconded. The point of noreturn is precisely to reject less. The no-return type has no values which means reading or writing that type is merely undefined behavior. It doesn't have to be rejected. void x; // special cased to be rejected. noreturn x; // totally fine, just don't read or write to it. with noreturn (and removing void/ or replacing it with unit) the statement any type may be used to create a variable of that type is true. Leading to less special casing and possible contradictions in the type system. How the backend may deal with a variable such as this, is actually easy. just ignore it. It doesn't need any storage and will never be referenced in well formed code.
Oct 15 2021
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 15 October 2021 at 19:10:42 UTC, Stefan Koch wrote:
 On Friday, 15 October 2021 at 18:59:55 UTC, Timon Gehr wrote:
 On 10/15/21 8:52 PM, Basile B. wrote:
 
 No, you can use it everywhere a Type can be used: cast to 
 type, TemplateTypeParameter, VarDeclaration type, etc. And 
 most of the time this is obviously not useful and has to be 
 rejected, i.e during the semantic, i.e by the front-end.
I don't think rejecting everything that's "obviously not useful" is the right approach. It just causes headaches for generic code.
Seconded. The point of noreturn is precisely to reject less. The no-return type has no values which means reading or writing that type is merely undefined behavior. It doesn't have to be rejected.
It *shouldn't* be undefined behavior. What `noreturn` means is, "evaluation of this expression does not terminate normally". Some examples of `noreturn` expressions include: - `assert(0)` - `throw new Exception("error")` - `{ while (1) {} }()` The meaning of any *specific* `noreturn` expression is determined by the semantic rules given for that expression in the language spec--i.e., it is done on a case-by-case basis. In the case of accessing a `noreturn` variable, the relevant semantic rule is given [in DIP 1034][1]: evaluating an expression that reads or writes a variable of type `noreturn` has the same behavior as evaluating the expression `assert(0)`. [1]: https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1034.md#interaction-with-other-language-features
Oct 15 2021
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Friday, 15 October 2021 at 19:10:42 UTC, Stefan Koch wrote:
 Seconded. The point of noreturn is precisely to reject less.
 The no-return type has no values which means reading or writing 
 that type is merely undefined behavior. It doesn't have to be 
 rejected.
If we ignore the DIP, couldn't you just as well claim that you should be able to assume anything about a type that is never instantiated as long as the assumption does not lead to a contradiction?
Oct 15 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 16 October 2021 at 05:27:42 UTC, Ola Fosheim Grøstad 
wrote:
 On Friday, 15 October 2021 at 19:10:42 UTC, Stefan Koch wrote:
 Seconded. The point of noreturn is precisely to reject less.
 The no-return type has no values which means reading or 
 writing that type is merely undefined behavior. It doesn't 
 have to be rejected.
If we ignore the DIP, couldn't you just as well claim that you should be able to assume anything about a type that is never instantiated as long as the assumption does not lead to a contradiction?
Even if you, the programmer, could make such assumptions, the compiler cannot, because proving that a type is never instantiated is halting-equivalent. `noreturn`, on the other hand, is defined in the language spec to be impossible to instantiate, so the compiler does not need to prove anything.
Oct 16 2021
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Saturday, 16 October 2021 at 13:40:23 UTC, Paul Backus wrote:
 Even if you, the programmer, could make such assumptions, the 
 compiler cannot, because proving that a type is never 
 instantiated is halting-equivalent.
This would make sense if you made the claim "arbitrary type" and "any possible program", but this is not what I meant.
 `noreturn`, on the other hand, is defined in the language spec 
 to be impossible to instantiate, so the compiler does not need 
 to prove anything.
No, I meant, if you inject a "noreturn" type into generic code; Then you know that it will never be instantiated and could allow any assumptions about it to be true as long as the complete set of "granted" assumptions does not lead to a contradiction.
Oct 16 2021
prev sibling parent MoonlightSentinel <moonlightsentinel disroot.org> writes:
On Friday, 15 October 2021 at 18:52:31 UTC, Basile B. wrote:
 No, you can use it everywhere a Type can be used: cast to type, 
 TemplateTypeParameter, VarDeclaration type, etc. And most of 
 the time this is obviously not useful and has to be rejected, 
 i.e during the semantic, i.e by the front-end.
That's actually not the intention of the DIP, only a few usages of `noreturn` are treated as an compiler error. E.g. `noreturn var` is fine as long as the code never accesses the variable - which is useful for templated code like `auto foo = possiblyNoreturnFunc()`.
Oct 15 2021
prev sibling next sibling parent MoonlightSentinel <moonlightsentinel disroot.org> writes:
On Friday, 15 October 2021 at 17:13:29 UTC, Andrei Alexandrescu 
wrote:
 It has been predicted by several folks that noreturn has very 
 limited utility and a variety of weird corner cases that will 
 cause a hecatomb of complications in the language, the standard 
 library, and the implementation.
This is a claim that has yet to be seen. Most of the complications stem from the fact that the introduction of `noreturn` allows DMD to detect unreachable code more accurately. This caused several `statement not reachable` warnings in Phobos (and other libraries) because templated functions are now inferred as `noreturn` instead of `void`. But that problem is not specific to `noreturn` and instead a general issue for templated code.
 (For starters: how the hell is "will not return" a type that's 
 supposed to do all or at least most things that types do, when 
 even the phrase defining it has no subject? It's about a 
 FUNCTION that won't return, so it should be an attribute 
 applicable to a FUNCTION!)
The name is misleading, `noreturn` represents the bottom type in general. But arguing about `noreturn` vs. `Nothing` vs. `<your choice here>` is just bikeshedding.
 In spite of these voices of reason, noreturn proceeded with a 
 definition and implementation as expected: execrable.
 
 [...]

 Ten opened bugs.
The DIP is actually rather precise in it's definition and reasoning. The implementation included in the current release is incomplete and in a pretty bad state. Walter started implementing some very specific basics (e.g. `noreturn exit()`) and left of without adding tests for the examples / requirements specified by the DIP (many of them are rejected or crash dmd). This is especially problematic because `noreturn` was introduced without a `-preview` flag. But at least the next release should be a lot better, I've already fixed several of the aforementioned issues and proposed an implementation for the missing aspects of that DIP.
 I was looking today through the wretched isAutodecodableString 
 (how did we ever think /that/ was a good idea?) and found a 
 number of weird occurrences of noreturn. Where? Of course, 
 where you least expect or want it.
That one is indeed nasty but mostly stems from the fact that `isAutodecodableString` accepts any type convertible to `[d]char[]` (except static arrays for some reason). That traits is a decent example of an overly general implementation.
 This is literally why we can't have nice things. If all that 
 investment went to literally any other proposal, it would be a 
 net positive.
This exaggeration is wrong and definitely uncalled for
Oct 15 2021
prev sibling next sibling parent Dennis <dkorpel gmail.com> writes:
On Friday, 15 October 2021 at 17:13:29 UTC, Andrei Alexandrescu 
wrote:
 Thirty-seven `noreturn` occurrences in phobos.

 Ten opened bugs.
Some context for this: On February 16 this year, Mike Parker informed me that [DIP1034](dlang.org/dips/1034) was accepted and we were discussing the implementation at that time.
 Do you plan to implement the feature or should I hand this off 
 to Andrew & Razvan to oversee it?
A fews days later, the first PR appeared by Walter, [DIP1034: add typeof(*null)](https://github.com/dlang/dmd/pull/12209). From this it looked like Walter was going to take care of it, so that conversation ended there. However, after implementing `noreturn exit()` through his favorite style of incremental improvements, Walter abandoned it, leaving DIP1034 in like a <10% implemented state. No thorough testing, no specification, no `throw` expressions, no `typeof([])`, no new `typeof(null)`, no core.demangle support, etc. Then a new dmd release came, which included the new `noreturn` type in object.d without any preview switches, but still totally unfinished. Of course bug reports were going to come. Later, other people (among which MoonlightSentinel) started picking up the loose ends by chasing down those bugs. They stumbled on the misfeature that is 'warnings', particularly the "statement not reachable" one. The compiler became smarter about unreachable code, e.g. `case X: exit(); break;` now generates a warning about the unreachable `break` statement. These warnings also manifest in some of Phobos' generic code, and since some people compile with `-w` which treats warnings as errors, this spawned some special case `static if` for `noreturn`. IMO it's better to get rid of that awful warning (at the very least in generic code) so those cases can be removed. I haven't looked too deep at `isAutodecodableString`, but it may say more about auto decoding than `noreturn`. There's already this ugly signature: ``` void popFront(T)(scope ref inout(T)[] a) safe pure nothrow nogc if (!isAutodecodableString!(T[]) && !is(T[] == void[])) ```
Oct 15 2021
prev sibling next sibling parent Dukc <ajieskola gmail.com> writes:
On Friday, 15 October 2021 at 17:13:29 UTC, Andrei Alexandrescu 
wrote:
 It has been predicted by several folks that noreturn has very 
 limited utility and a variety of weird corner cases that will 
 cause a hecatomb of complications in the language, the standard 
 library, and the implementation.
It does have teething problems, yes. But I still think the concept itself is sound.
 (For starters: how the hell is "will not return" a type that's 
 supposed to do all or at least most things that types do, when 
 even the phrase defining it has no subject? It's about a 
 FUNCTION that won't return, so it should be an attribute 
 applicable to a FUNCTION!)
Wrong by a kilometer. It is supposed to be a bottom type - the subtype of all other types that can be referenced but never instantiated. Marking functions as not returning is only one use for it. It's also used to mark a pointer as always null, or a range or a container as always empty. Now granted, the name may lead to think otherwise. But I don't think it's any stranger than, say `const` that really means "read only" in D.
 In spite of these voices of reason, noreturn proceeded with a 
 definition and implementation as expected: execrable.
Excepted? Yes. But because of the youth of the feature, not because of a bad concept. Compare `noreturn` to rest of the language in 2009 or so, then it's likely to be a fair comparison.
 I was looking today through the wretched isAutodecodableString 
 (how did we ever think /that/ was a good idea?) and found a 
 number of weird occurrences of noreturn. Where? Of course, 
 where you least expect or want it.
It kind of makes sense, save for autodecoding existing in first place. Autodecoding is supposed to apply on arrays of elements that convert to characters, and `noreturn` does. Theoretically, it does not matter whether `noreturn[]` decodes or no, because it's always empty anyway. In practice special casing it to not decode was probably the easiest way to fix the resulting compile errors, plus now `isRandomAccessRange!(noreturn[]) == true`
 This is literally why we can't have nice things. If all that 
 investment went to literally any other proposal, it would be a 
 net positive.
Redirecting the effort to the worst DIPs would not have been even close to net positive. There have been proposals that would have left language in _worse_ state than before even after stabilizing. You may be right that this one brings only trivial benefits, but it is far from a net negative. You might convince me that even sound new features in the reviews should have larger benefits. But this one has already gone through the reviews and implementation, and is being stabilized all the time. Plus it fits nicely in the language once stable. I say it's worth it's weight to keep from now on.
Oct 15 2021
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Oct 15, 2021 at 01:13:29PM -0400, Andrei Alexandrescu via Digitalmars-d
wrote:
 It has been predicted by several folks that noreturn has very limited
 utility and a variety of weird corner cases that will cause a hecatomb
 of complications in the language, the standard library, and the
 implementation.
Besides its weird name (it's really a bottom type than anything else; calling it noreturn is somewhat misleading), what exactly is wrong with it? T -- A mathematician learns more and more about less and less, until he knows everything about nothing; whereas a philospher learns less and less about more and more, until he knows nothing about everything.
Oct 15 2021
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10/15/21 11:48 PM, H. S. Teoh wrote:
 On Fri, Oct 15, 2021 at 01:13:29PM -0400, Andrei Alexandrescu via
Digitalmars-d wrote:
 It has been predicted by several folks that noreturn has very limited
 utility and a variety of weird corner cases that will cause a hecatomb
 of complications in the language, the standard library, and the
 implementation.
Besides its weird name (it's really a bottom type than anything else; calling it noreturn is somewhat misleading), what exactly is wrong with it? T
- What's in DMD is an incomplete implementation of DIP 1034, often resulting in ICEs. - It gives more static information to the compiler, exacerbating the issue of dead code warnings in generic code. - Existing template constraints sometimes don't deal with the new type correctly. - It implicitly converts to everything, so you can have ambiguous overloads: ```d void foo(int x){ } void foo(string x){ } void main(){ foo(assert(0)); } ``` I suspect that's ultimately the underlying issue why isInputRange!(noreturn[]) was false. (noreturn[] was treated as an autodecodable string due to some questionable design decisions in Phobos, but then it was not clear which overload of `decode` to use. Is `noreturn[]` encoded in utf-8 or utf-16? The answer is both.)
Oct 15 2021
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Oct 16, 2021 at 12:15:35AM +0200, Timon Gehr via Digitalmars-d wrote:
[...]
 - What's in DMD is an incomplete implementation of DIP 1034, often
   resulting in ICEs.
That's unfortunate, but I suppose better than the worst scenario Andrei first thought. :-P
 - It gives more static information to the compiler, exacerbating the
   issue of dead code warnings in generic code.
But shouldn't dead code in generic code just be elided, instead of eliciting warnings?
 - Existing template constraints sometimes don't deal with the new type
   correctly.
 - It implicitly converts to everything, so you can have ambiguous overloads:
 
 ```d
 void foo(int x){ }
 void foo(string x){ }
 
 void main(){ foo(assert(0)); }
 ```
Haha, unexpected interactions with a new feature: the plague of every language designer. :-D
 I suspect that's ultimately the underlying issue why
 isInputRange!(noreturn[]) was false. (noreturn[] was treated as an
 autodecodable string due to some questionable design decisions in
 Phobos, but then it was not clear which overload of `decode` to use.
 Is `noreturn[]` encoded in utf-8 or utf-16? The answer is both.)
I think I can guess the cause of this one. Phobos checks for implicit conversion to dchar, which before the advent of noreturn applied only to char, wchar, and dchar. But now noreturn comes along and implicit converts to everything, so the sig constraint incorrectly thinks that it must be one of the char types. Past this point, it becomes a train wreck as noreturn crashes through code that was expecting char, wchar, and dchar but getting noreturn instead. T -- If the comments and the code disagree, it's likely that *both* are wrong. -- Christopher
Oct 15 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 16 October 2021 at 02:56:02 UTC, H. S. Teoh wrote:
 On Sat, Oct 16, 2021 at 12:15:35AM +0200, Timon Gehr via
 - It gives more static information to the compiler, 
 exacerbating the
   issue of dead code warnings in generic code.
But shouldn't dead code in generic code just be elided, instead of eliciting warnings?
Unfortunately, the "statement is unreachable" warning cannot tell the difference between generic code and non-generic code, and has thus far resisted all attempts to teach it. Personally I think removing it altogether would be a net gain for the language. If anyone really wants it, we can always implement it as a check in D-Scanner or some similar tool.
Oct 15 2021
parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Saturday, 16 October 2021 at 04:04:29 UTC, Paul Backus wrote:
 On Saturday, 16 October 2021 at 02:56:02 UTC, H. S. Teoh wrote:
 On Sat, Oct 16, 2021 at 12:15:35AM +0200, Timon Gehr via
 - It gives more static information to the compiler, 
 exacerbating the
   issue of dead code warnings in generic code.
But shouldn't dead code in generic code just be elided, instead of eliciting warnings?
Unfortunately, the "statement is unreachable" warning cannot tell the difference between generic code and non-generic code, and has thus far resisted all attempts to teach it. Personally I think removing it altogether would be a net gain for the language. If anyone really wants it, we can always implement it as a check in D-Scanner or some similar tool.
Yes. Many other languages take that approach
Oct 16 2021
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
After skimming the DIP, I must apologize for being hasty.

By just looking at the bugs posted and the Phobos changes, it seemed to 
me (wrongly, fortunately) that noreturn has had an incomplete definition 
and was sprawling as corner cases were discovered. Whereas there may be 
valid concerns with regard to its usefulness, at the very minimum any 
critique would need to debate the definition laid down in the DIP with 
laudable care.
Oct 15 2021