digitalmars.D - proposed noreturn attribute
- Walter Bright (21/21) Jul 08 2017 C compilers (and by extension C++ compilers) usually have an extension w...
- Nicholas Wilson (13/38) Jul 08 2017 On the contrary I think attributes are wonderful, and I have a
- Walter Bright (7/9) Jul 08 2017 One could argue that since "noreturn" changes the interface of a functio...
- bachmeier (9/11) Jul 08 2017 Why should this be an attribute rather than a pragma? It looks to
- Andrei Alexandrescu (2/9) Jul 08 2017 So it's part of the summary of the function. -- Andrei
- bachmeier (5/14) Jul 08 2017 But it's additional clutter being added for the benefit of the
- Random D user (14/23) Jul 11 2017 If it feels like a pragma, should be part of the function and
- Jack Stouffer (3/4) Jul 08 2017 I agree. There's no reason I can think of as to why the no-return
- Andrei Alexandrescu (3/8) Jul 08 2017 If present in the signature of the function you can figure that the
- Walter Bright (5/10) Jul 08 2017 Separate compilation. I.e. if one changes the implementation of a functi...
- Andrei Alexandrescu (15/16) Jul 08 2017 An attribute is fine. A more PL-minded possibility is to return a
- Stefan Koch (7/24) Jul 08 2017 ... since it's going to be special cased by the compiler we might
- Andrei Alexandrescu (8/12) Jul 08 2017 Walter and I have repeated experience with moving compiler smarts into
- Stefan Koch (8/10) Jul 08 2017 I do get your point.
- Andrei Alexandrescu (8/20) Jul 08 2017 Yah, there's marginal utility only - improves code generation and makes
- John Colvin (4/21) Jul 08 2017 I wonder if some lessons from Haskell's "bottom" type would be
- Andrei Alexandrescu (3/5) Jul 08 2017 Affirmative. The nice touch of bottom (heh) is that it's convertible to
- Nick Sabalausky (Abscissa) (2/3) Jul 09 2017 Where's D's PC-police now?
- Walter Bright (6/11) Jul 08 2017 That is a scathingly brilliant idea. It has another nice effect - the co...
- H. S. Teoh via Digitalmars-d (19/33) Jul 08 2017 [...]
- Walter Bright (4/13) Jul 08 2017 There's no subtlety to it. It's a function that never returns. I.e. it d...
- H. S. Teoh via Digitalmars-d (11/21) Jul 08 2017 [...]
- Lurker (2/9) Jul 13 2017 The reverse is also true, and more disastrous!
- Vladimir Panteleev (6/11) Jul 08 2017 You meant static here I guess.
- Vladimir Panteleev (3/5) Jul 08 2017 Oops, never mind that - this makes the function uncallable.
- Andrei Alexandrescu (24/38) Jul 08 2017 Eh, interesting. Indeed this doesn't compile anymore:
- Martin Nowak (6/17) Jul 08 2017 That's a lot more complex (for the compiler and to explain) than
- Walter Bright (6/10) Jul 08 2017 We have types that cannot be named (Voldemort types), types that have no...
- crimaniak (5/7) Jul 08 2017 I'm afraid it's perpendicular to type theory - every type
- sarn (9/16) Jul 08 2017 In pure functional languages, that's what "bottom" or Haskell's
- Walter Bright (3/22) Jul 08 2017 Thanks, it looks like we won't get any help from type theory. We're on o...
- Meta (22/23) Jul 08 2017 Just so we are all on the same page, from a type-theory
- Meta (4/27) Jul 08 2017 In C++/D terms, the unit type could be expressed as `enum Unit {
- Walter Bright (7/34) Jul 08 2017 I read that a Void function in Haskell does not return. Is that incorrec...
- Timon Gehr (12/29) Jul 09 2017 It is indeed incorrect, but this has little relevance for D. In Haskell,...
- Walter Bright (2/3) Jul 09 2017 Thanks!
- Walter Bright (4/26) Jul 08 2017 That is indeed an interesting idea. Thanks!
- Andrei Alexandrescu (3/10) Jul 09 2017 We should use typeof(assert(0)) for Bottom. There is precedent - there
- Stefan Koch (3/13) Jul 09 2017 I agree with is approach.
- Walter Bright (5/7) Jul 09 2017 I had forgotten about the typeof(null) thing. You're right. But there ar...
- Stefan Koch (3/10) Jul 09 2017 Not valid.
- Andrei Alexandrescu (11/20) Jul 09 2017 That would be a pointer that may only be null - a consequence of the
- Meta (4/24) Jul 09 2017 `typeof(null)` actually has one valid value and doesn't crash the
- Andrei Alexandrescu (3/33) Jul 09 2017 Agreed (with the mention it's not in contradiction with the above). --
- Meta (26/34) Jul 10 2017 That was in response to:
- Timon Gehr (2/5) Jul 11 2017 Bottom is just a regular type.
- Meta (3/8) Jul 11 2017 It's a regular type with unusual behaviour due to it being
- Timon Gehr (2/11) Jul 12 2017 All types are unusual. That does not mean they are not all types.
- Andrei Alexandrescu (3/9) Jul 11 2017 Wouldn't the fact that it's a subtype of all other types make it a bit
- Timon Gehr (11/21) Jul 12 2017 Every type is peculiar. That's essentially the point of having types.
- Walter Bright (30/30) Jul 09 2017 Let's call the bottom type 'B' for the moment, for convenience.
- Timon Gehr (10/56) Jul 11 2017 I don't see why to add this special case.
- Walter Bright (3/4) Jul 09 2017 That also leaves the door open for:
- Olivier FAURE (4/8) Jul 10 2017 I would really prefer noreturn (or noreturn_t) to be the default
- Andrei Alexandrescu (12/36) Jul 08 2017 How does the information provided lead to such a conclusion? There's
- H. S. Teoh via Digitalmars-d (16/27) Jul 08 2017 Technically, the *type* itself exist, it's just that you cannot create
- Nick Sabalausky (Abscissa) (20/39) Jul 09 2017 Pro:
- Jonathan Marler (14/39) Jul 08 2017 Some questions...
- Mr.D (4/5) Jul 09 2017 What about
- Daniel N (6/11) Jul 09 2017 void func()
- Steven Schveighoffer (3/18) Jul 09 2017 Yes, this!
- Nicholas Wilson (5/24) Jul 09 2017 That certainly signals intention, but I still think that an
- Steven Schveighoffer (12/36) Jul 09 2017 This whole proposal is compiler voodoo :)
- Nicholas Wilson (12/51) Jul 09 2017 If the compiler is like a magician, adding an attribute is like
- Andrei Alexandrescu (4/44) Jul 09 2017 I wonder why there's so much attraction to exotic approaches when
- Steven Schveighoffer (12/16) Jul 09 2017 Your definition of exotic differs from mine.
- Andrei Alexandrescu (4/12) Jul 09 2017 Mine is closest to: "strikingly, excitingly, or mysteriously different
- Andrei Alexandrescu (4/7) Jul 09 2017 That's already a hack on top of a hack.
- Steven Schveighoffer (8/15) Jul 09 2017 An out contract should apply even when contracts are removed. The
- Andrei Alexandrescu (3/4) Jul 09 2017 I wouldn't argue that. I do argue it's a hack compared to the principled...
- Stefan Koch (7/11) Jul 09 2017 Adding a new builtin-type would also be okay.
- Meta (79/83) Jul 09 2017 I thought some more about the ramifications of having a Bottom
- Meta (5/18) Jul 09 2017 Another case that we should probably just statically disallow:
- Walter Bright (2/7) Jul 09 2017 Wouldn't `immutable(Bottom)` simply resolve to `Bottom`?
- Meta (25/33) Jul 09 2017 I was thinking about that (and in fact, making Bottom a "viral"
- Timon Gehr (3/7) Jul 12 2017 Sorry, but this thinking has no place in type system design. This is
- Meta (5/12) Jul 12 2017 D is not ML or Haskell or Idris. Rust has trod this ground before
- Marc =?UTF-8?B?U2Now7x0eg==?= (3/6) Jul 19 2017 Only to turn around after they realized their mistake:
- Meta (10/16) Jul 19 2017 That page doesn't say anything about passing ! as a type to
- Andrei Alexandrescu (25/40) Jul 12 2017 Timon, I think you're very well positioned to author a DIP on this.
- Timon Gehr (34/82) Jul 13 2017 I might do that, however there are a couple of open questions
- Timon Gehr (4/9) Jul 13 2017 (Actually, there are some complications like the .sizeof
- Andrei Alexandrescu (2/12) Jul 13 2017 I wonder if sizeof could be made size_t.max. -- Andrei
- Walter Bright (2/14) Jul 15 2017 I thought bottom.sizeof would be 0.
- Timon Gehr (22/38) Jul 16 2017 0 is the obvious size for the unit type (the type with a single value,
- Guillaume Boucher (4/5) Jul 16 2017 True, but size_t.max doesn't have the properties of ∞.
- Timon Gehr (2/9) Jul 16 2017 In this case, Bottom.sizeof is a value of type Bottom, which must not ex...
- Andrei Alexandrescu (6/17) Jul 16 2017 Yah that's not workable. Generally the fewer odd exceptions the better,
- Guillaume Boucher (5/15) Jul 16 2017 It doesn't exist during runtime, but there's no problem to just
- Walter Bright (2/3) Jul 17 2017 T.sizeof is a value of type size_t, not type T.
- Walter Bright (4/21) Jul 17 2017 But if Bottom does not exist, then S doesn't exist either, and hence the...
- Timon Gehr (10/16) Jul 17 2017 The reason division by 0 is left undefined is that instead saying 1/0 =
- Walter Bright (3/13) Jul 17 2017 Infinity makes sense for 1/0, but I don't see how that automatically tra...
- Timon Gehr (2/17) Jul 17 2017 It does not. The reasoning is unrelated.
- John Colvin (14/37) Jul 18 2017 Strictly speaking it just shouldn't have a sizeof, because sizeof
- Stefan Koch (2/3) Jul 18 2017 new Type(TYENUM.Tint32); :o)
- Timon Gehr (3/5) Jul 18 2017 That's not an option. Bottom is a subtype of all types. It cannot remove...
- Stefan Koch (5/10) Jul 18 2017 Timon, how important is it to actually have bottom ?
- Timon Gehr (42/54) Jul 18 2017 D has a C-inspired first-order type system, so it is not necessarily
- Yuxuan Shui (2/10) Jul 18 2017 What about void?
- Timon Gehr (8/21) Jul 18 2017 You can't have a value of type void, but it is not empty either.
- Moritz Maxeiner (6/25) Jul 18 2017 Could you explain why `return foo();` is even legal for a `void
- Adam D. Ruppe (6/8) Jul 18 2017 Suppose you are writing a template function that forwards:
- Moritz Maxeiner (2/10) Jul 18 2017 That's a good pragmatic (syntactic sugar) reason, thanks.
- Timon Gehr (7/13) Jul 18 2017 Because the ad-hoc decision to make void a type that is not really a
- Andrei Alexandrescu (4/20) Jul 16 2017 My thinking comes from bottom being the subtype of all types in the
-
Andrei Alexandrescu
(13/49)
Jul 16 2017
- Olivier FAURE (28/43) Jul 17 2017 I'd really prefer if you avoided the whole `typeof(assert(0))`
- Patrick Schluter (2/50) Jul 17 2017 Yes, this!
- Andrei Alexandrescu (9/12) Jul 17 2017 Noted, thanks. I won't debate this much but for now I disagree. The "no
- H. S. Teoh via Digitalmars-d (26/37) Jul 17 2017 [...]
- Stefan Koch (3/47) Jul 17 2017 +1000!
- Andrei Alexandrescu (6/10) Jul 17 2017 There is no reason to over-react seeing as the option of adding a name
- H. S. Teoh via Digitalmars-d (17/29) Jul 17 2017 Actually, I don't really care enough about this issue to want it to be
- Seb (11/25) Jul 17 2017 I can't agree more. This is textbook procrastination and
-
Brad Roberts via Digitalmars-d
(3/13)
Jul 17 2017
Semi-valid, but
- Walter Bright (26/36) Jul 18 2017 The issue here (for me) is to have a type system that matches type syste...
- Petar Kirov [ZombineDev] (5/38) Jul 18 2017 Agreed. Discovered vs invented as Philip Wadler classifies the
- Nicholas Wilson (37/59) Jul 18 2017 I would understand it to mean that if it were to return, foo
- Walter Bright (13/26) Jul 18 2017 That's the C++ behavior. I know we are all accustomed to it and hence th...
- Nicholas Wilson (6/15) Jul 18 2017 It describe the behaviour of the function: I think it is neither
- Walter Bright (10/18) Jul 18 2017 In the glue code, replace:
- Nicholas Wilson (5/33) Jul 17 2017 (3) LDC and GDC already have an implementation in the form of an
- Atila Neves (4/13) Jul 18 2017 object.d:
- Olivier FAURE (3/9) Jul 18 2017 Fair enough.
- Marc =?UTF-8?B?U2Now7x0eg==?= (5/10) Jul 19 2017 That sounds more like a top type, though, because as you said it
- Stefan Koch (3/14) Jul 19 2017 It's the bottom.
- Timon Gehr (2/17) Jul 19 2017 No. Bottom is to types as typeof(null) is to class types.
- Stefan Koch (4/5) Jul 19 2017 I fear you lost me again :)
- Timon Gehr (3/11) Jul 19 2017 It is the other way around. Bottom is a subtype of every type / a value
- Moritz Maxeiner (4/6) Jul 19 2017 AFAIK from type theory, bottom is defined as having no values (so
- Marco Leise (20/27) Jul 19 2017 2018, Dlang is now an esoteric language. After a long
- Timon Gehr (2/9) Jul 19 2017 https://en.wikipedia.org/wiki/Principle_of_explosion
- Moritz Maxeiner (5/14) Jul 19 2017 I am aware, but once a statement (and its negation) can be
- Timon Gehr (3/17) Jul 19 2017 I disagree with both of those statements, but I'm not sure how any of
- Moritz Maxeiner (16/35) Jul 19 2017 The sentence I quoted states you can use a value of type bottom
- Timon Gehr (17/53) Jul 19 2017 What I said does not /use/ the principle of explosion; it states it.
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (5/15) Jul 19 2017 As I understand it, you can create *variables* of type Bottom but not
- Timon Gehr (4/21) Jul 20 2017 Within the function body, it is.
- Andrei Alexandrescu (3/12) Jul 21 2017 Didn't know "false implies anything" comes with such a nice name.
- w0rp (9/9) Jul 24 2017 I didn't look through all of the replies to this thread to check
- Yuxuan Shui (3/18) Jul 20 2017 Actually, Object should be considered the Top type. All Classes
- Timon Gehr (4/15) Jul 19 2017 There is nothing that can be anything, but anything can be top.
- Walter Bright (2/3) Jul 09 2017 Some great info and links. It's a compelling argument to add a bottom ty...
- Andrei Alexandrescu (5/6) Jul 09 2017 Speaking of which, I think we shouldn't give it a name, same as
- Andrei Alexandrescu (12/19) Jul 09 2017 And btw this is technically a breaking change because somebody somewhere...
- Steven Schveighoffer (4/8) Jul 09 2017 I would argue a type hack in the compiler doesn't compare to the
- H. S. Teoh via Digitalmars-d (13/18) Jul 09 2017 I like out{ assert(0); } for pretty much the same reasons as Steven
- Nick Sabalausky (Abscissa) (4/21) Jul 09 2017 Prioritizing "path of least resistense" over "this is cleaner and more
- Nicholas Wilson (50/81) Jul 09 2017 While I agree with your sentiment in principle (heh), we need to
- Steven Schveighoffer (8/16) Jul 10 2017 I'm pretty much giving up on arguing in this thread, as it seems people
- Walter Bright (4/6) Jul 10 2017 It is useful anywhere dataflow analysis is useful.
- Steven Schveighoffer (24/32) Jul 10 2017 That doesn't require static introspection. The compiler can see that,
- Meta (14/37) Jul 10 2017 Currently not. This is either a bug or the compiler's flow
- Walter Bright (4/6) Jul 10 2017 Flow analysis relies on the function's signature, not its implementation...
- Steven Schveighoffer (29/61) Jul 10 2017 I think the implication from Walter is that f would be treated just like...
- Meta (6/24) Jul 11 2017 I think that for our own sanity the dead-code check would have to
- Guillaume Boucher (40/44) Jul 14 2017 I don't think that's true. A Bottom type does not cover all use
- Lurker (11/16) Jul 14 2017 No matter how you look at it, this code should simply not be
- Andrei Alexandrescu (8/27) Jul 14 2017 Conventional thinking has it that derived methods should "require less
- Timon Gehr (12/70) Jul 16 2017 I'd say a function with return type Bottom can override any function in
- Guillaume Boucher (18/32) Jul 16 2017 The best you can hope for is that any code with pragma(noreturn)
- Timon Gehr (5/16) Jul 16 2017 The issue isn't purism, it is type safety. If you create an
- Guillaume Boucher (6/10) Jul 16 2017 I see it as some kind of weak guarantee, where the compiler can
- Walter Bright (9/11) Jul 19 2017 Pragmas are not supposed to change the semantics of the code, they are i...
- Jacob Carlborg (9/13) Jul 10 2017 I highly doubt that the compiler does not need to be changed at all to
- H. S. Teoh via Digitalmars-d (9/24) Jul 10 2017 I did not say the compiler does not need to be changed. I said the
- bachmeier (5/7) Jul 09 2017 Doesn't the compiler know about an out contract even with
- Nick Sabalausky (Abscissa) (3/19) Jul 09 2017 Too indirect and verbose. An attribute or special "return type" would
- H. S. Teoh via Digitalmars-d (38/58) Jul 10 2017 If DIP 1009 is accepted, this would not be verbose at all:
- Patrick Schluter (2/62) Jul 10 2017 +1
- Walter Bright (2/3) Jul 09 2017 It needs to be part of the function signature, not the function implemen...
- Jacob Carlborg (7/17) Jul 10 2017 I'm going to ask the stupid question: I understand that this is for the
- ketmar (10/14) Jul 10 2017 the case that makes @noreturn worth having is even not optimizing, but
- Walter Bright (2/3) Jul 10 2017 Yes. Not having it makes enforce(), for example, generate poor code.
- Jacob Carlborg (4/5) Jul 10 2017 Not sure I understand. "enforce" will return if the condition is true.
- Iain Buclaw via Digitalmars-d (4/7) Jul 11 2017 Right, you can't put noreturn on enforce itself, but you can on the
- Jacob Carlborg (5/7) Jul 11 2017 Ah, but I though it would just contain a "throw" for the case the the
- Marco Leise (12/19) Jul 10 2017 in=20
- Rainer Schuetze (22/41) Jul 11 2017 I don't like the inflation of attributes, too, but encoding it into the
C compilers (and by extension C++ compilers) usually have an extension which allows a function to be marked as one that never returns. The point of this is it enables improved data flow analysis and better code being generated. Noreturn functions crop up in things like assert's and enforce's. DMD internally hardcodes a few functions it knows about that are noreturn, and the DMD optimizer and codegen take advantage of it. But when people write their own assert's and enforce's, this falls apart. While the programs will still work, they won't be as efficient as they could be. Having an noreturn attribute will take care of that: noreturn void ThisFunctionExits(); Yes, it's another builtin attribute and attributes are arguably a failure in language design. Has anyone a better idea? Does anyone want to write a DIP for this? Example: DMC uses a pragma to do it: void ThisFunctionExits(); #pragma noreturn(ThisFunctionExits); GCC uses an attribute: void ThisFunctionExits() __attribute__ ((__noreturn__)); VC uses: __declspec(noreturn) void ThisFunctionExits();
Jul 08 2017
On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:C compilers (and by extension C++ compilers) usually have an extension which allows a function to be marked as one that never returns. The point of this is it enables improved data flow analysis and better code being generated. Noreturn functions crop up in things like assert's and enforce's. DMD internally hardcodes a few functions it knows about that are noreturn, and the DMD optimizer and codegen take advantage of it. But when people write their own assert's and enforce's, this falls apart. While the programs will still work, they won't be as efficient as they could be. Having an noreturn attribute will take care of that: noreturn void ThisFunctionExits(); Yes, it's another builtin attribute and attributes are arguably a failure in language design.On the contrary I think attributes are wonderful, and I have a DIP in the pipeline to address some the the problems (https://github.com/dlang/DIPs/pull/75)Has anyone a better idea? Does anyone want to write a DIP for this? Example: DMC uses a pragma to do it: void ThisFunctionExits(); #pragma noreturn(ThisFunctionExits); GCC uses an attribute: void ThisFunctionExits() __attribute__ ((__noreturn__)); VC uses: __declspec(noreturn) void ThisFunctionExits();consider that GDC and LDC already both have that attribute courtesy of their backends ( attribute("noreturn") and llvmAttr("noreturn") respectively). While it may seem a good idea to unify them, if people want performance then they will use either GDC or LDC, which already have that attribute. So I would question the usefulness of such an attribute but won't go so far as to discourage anyone from trying. However it should be a compiler dependant alias in core.attribute if it is to become a thing.
Jul 08 2017
On 7/8/2017 3:52 AM, Nicholas Wilson wrote:consider that GDC and LDC already both have that attribute courtesy of their backends ( attribute("noreturn") and llvmAttr("noreturn") respectively).One could argue that since "noreturn" changes the interface of a function, it should also change its signature (i.e. name mangling). I don't believe this is currently the case with any C++ compiler, or with LDC/GDC. And frankly I'm sure it's annoying to users to use that attribute portably between GDC/LDC. The concept of noreturn should be part of the language, not a compiler implementation.
Jul 08 2017
On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:Having an noreturn attribute will take care of that: noreturn void ThisFunctionExits();Why should this be an attribute rather than a pragma? It looks to me that the goal is to pass information to the compiler, and according to the spec: "Pragmas are a way to pass special information to the compiler and to add vendor specific extensions to D. Pragmas can be used by themselves terminated with a ‘;’, they can influence a statement, a block of statements, a declaration, or a block of declarations."
Jul 08 2017
On 7/8/17 7:07 AM, bachmeier wrote:On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:So it's part of the summary of the function. -- AndreiHaving an noreturn attribute will take care of that: noreturn void ThisFunctionExits();Why should this be an attribute rather than a pragma?
Jul 08 2017
On Saturday, 8 July 2017 at 12:18:38 UTC, Andrei Alexandrescu wrote:On 7/8/17 7:07 AM, bachmeier wrote:But it's additional clutter being added for the benefit of the compiler. Additions like this make it hard for those of us showing the language to others.On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:So it's part of the summary of the function. -- AndreiHaving an noreturn attribute will take care of that: noreturn void ThisFunctionExits();Why should this be an attribute rather than a pragma?
Jul 08 2017
On Saturday, 8 July 2017 at 12:18:38 UTC, Andrei Alexandrescu wrote:On 7/8/17 7:07 AM, bachmeier wrote:If it feels like a pragma, should be part of the function and reflectable, then how about: void assertFalse(bool cond) pragma(noreturn) or void assertFalse(bool cond) pragma("noreturn") The compiler could probably give an error if the "" (inside pragma) wasn't a known string. Also pragma would be useful as standard way of saying "special compiler attribute". No need to consume global attribute namespace. I'm expecting to see myproject_safe and myproject_noreturn type of attributes someday in some project :|On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:So it's part of the summary of the function. -- AndreiHaving an noreturn attribute will take care of that: noreturn void ThisFunctionExits();Why should this be an attribute rather than a pragma?
Jul 11 2017
On Saturday, 8 July 2017 at 11:07:32 UTC, bachmeier wrote:Why should this be an attribute rather than a pragma?I agree. There's no reason I can think of as to why the no-return should be part of the ABI.
Jul 08 2017
On 07/08/2017 01:14 PM, Jack Stouffer wrote:On Saturday, 8 July 2017 at 11:07:32 UTC, bachmeier wrote:If present in the signature of the function you can figure that the function won't return by introspection. -- AndreiWhy should this be an attribute rather than a pragma?I agree. There's no reason I can think of as to why the no-return should be part of the ABI.
Jul 08 2017
On 7/8/2017 10:14 AM, Jack Stouffer wrote:On Saturday, 8 July 2017 at 11:07:32 UTC, bachmeier wrote:Separate compilation. I.e. if one changes the implementation of a function such that it now returns, it will break any compiled code that relied on it being noreturn. This is why, for example, nothrow is part of the signature.Why should this be an attribute rather than a pragma?I agree. There's no reason I can think of as to why the no-return should be part of the ABI.
Jul 08 2017
On 7/8/17 6:15 AM, Walter Bright wrote:Has anyone a better idea? Does anyone want to write a DIP for this?An attribute is fine. A more PL-minded possibility is to return a specific type: struct None { disable this(); disable this(this); disable property None init(); } None ThisFunctionExits(); The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return. Andrei
Jul 08 2017
On Saturday, 8 July 2017 at 12:17:57 UTC, Andrei Alexandrescu wrote:On 7/8/17 6:15 AM, Walter Bright wrote:... since it's going to be special cased by the compiler we might as well hardwire a type called none. Although it seems to be that the scope of no-return is extremely narrow. Because we do have precisely builtin assert(0).Has anyone a better idea? Does anyone want to write a DIP for this?An attribute is fine. A more PL-minded possibility is to return a specific type: struct None { disable this(); disable this(this); disable property None init(); } None ThisFunctionExits(); The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return. Andrei
Jul 08 2017
On 07/08/2017 08:20 AM, Stefan Koch wrote:... since it's going to be special cased by the compiler we might as well hardwire a type called none.Walter and I have repeated experience with moving compiler smarts into library artifacts. Sometimes it's arguably not the best way to go, but in the majority of cases it's been beneficial for both the language and the compiler implementation.Although it seems to be that the scope of no-return is extremely narrow. Because we do have precisely builtin assert(0).What is the signature of a function that logs something to a file and ends in assert(0)? Andrei
Jul 08 2017
On Saturday, 8 July 2017 at 17:14:32 UTC, Andrei Alexandrescu wrote:What is the signature of a function that logs something to a file and ends in assert(0)?I do get your point. However I still doubt that this is worth a the additional language complexity. Usually the clue would be in the name. sth. like void logAndDie(string lastWords) { ... }
Jul 08 2017
On 7/8/17 1:30 PM, Stefan Koch wrote:On Saturday, 8 July 2017 at 17:14:32 UTC, Andrei Alexandrescu wrote:Yah, there's marginal utility only - improves code generation and makes a few awkward workarounds unnecessary. Yet many other languages and implementations have it, so apparently the utility is justifiable.What is the signature of a function that logs something to a file and ends in assert(0)?I do get your point. However I still doubt that this is worth a the additional language complexity.Usually the clue would be in the name. sth. like void logAndDie(string lastWords) { ... }Problem here being this code is opaque to compile-time analysis and to introspection. Unless, that is, the compiler understands the name (smell) or the introspection does heuristics on name - e.g. everything that ends in "andDie" does not return (smell). -- Andrei
Jul 08 2017
On Saturday, 8 July 2017 at 12:17:57 UTC, Andrei Alexandrescu wrote:On 7/8/17 6:15 AM, Walter Bright wrote:I wonder if some lessons from Haskell's "bottom" type would be relevant here.Has anyone a better idea? Does anyone want to write a DIP for this?An attribute is fine. A more PL-minded possibility is to return a specific type: struct None { disable this(); disable this(this); disable property None init(); } None ThisFunctionExits(); The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return. Andrei
Jul 08 2017
On 7/8/17 2:26 PM, John Colvin wrote:I wonder if some lessons from Haskell's "bottom" type would be relevant here.Affirmative. The nice touch of bottom (heh) is that it's convertible to anything, so you can use it in complex expressions as a wildcard. -- Andrei
Jul 08 2017
On 07/08/2017 02:37 PM, Andrei Alexandrescu wrote:nice touch of bottom (heh)Where's D's PC-police now?
Jul 09 2017
On 7/8/2017 5:17 AM, Andrei Alexandrescu wrote:None ThisFunctionExits(); The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return.That is a scathingly brilliant idea. It has another nice effect - the compiler doesn't have to diagnose: noreturn int foo(); as an error! Me like.
Jul 08 2017
On Sat, Jul 08, 2017 at 01:09:35PM -0700, Walter Bright via Digitalmars-d wrote:On 7/8/2017 5:17 AM, Andrei Alexandrescu wrote:[...] Hmmm. Just to clarify, what exactly does noreturn include? If I have a function that calls exit(), that's noreturn? What about a function that always throws? Or a function that calls exec()? A function that always ends in assert(0)? A function that context-switches to a different thread / fibre and terminates this one? Personally, I'm for this. It is useful for functions that constructs then throws an exception, for example. It would be nice to be able to call such a function from another function that returns non-void without having to insert assert(0) into the latter to avoid the compiler complaining that you failed to return a value. As for Andrei's idea, it's pretty clever but we would need to standardize the None type, otherwise we risk hard-to-read code when everyone rolls their own None type with different names. An attribute has the advantage that it will be universally understood. T -- It only takes one twig to burn down a forest.None ThisFunctionExits(); The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return.That is a scathingly brilliant idea. It has another nice effect - the compiler doesn't have to diagnose: noreturn int foo(); as an error!
Jul 08 2017
On 7/8/2017 1:20 PM, H. S. Teoh via Digitalmars-d wrote:Hmmm. Just to clarify, what exactly does noreturn include? If I have a function that calls exit(), that's noreturn? What about a function that always throws? Or a function that calls exec()? A function that always ends in assert(0)? A function that context-switches to a different thread / fibre and terminates this one?There's no subtlety to it. It's a function that never returns. I.e. it doesn't execute a 'RET' instruction.As for Andrei's idea, it's pretty clever but we would need to standardize the None type, otherwise we risk hard-to-read code when everyone rolls their own None type with different names. An attribute has the advantage that it will be universally understood.It would be like `size_t`.
Jul 08 2017
On Sat, Jul 08, 2017 at 01:20:03PM -0700, H. S. Teoh via Digitalmars-d wrote: [...]Personally, I'm for this. It is useful for functions that constructs then throws an exception, for example. It would be nice to be able to call such a function from another function that returns non-void without having to insert assert(0) into the latter to avoid the compiler complaining that you failed to return a value. As for Andrei's idea, it's pretty clever but we would need to standardize the None type, otherwise we risk hard-to-read code when everyone rolls their own None type with different names. An attribute has the advantage that it will be universally understood.[...] Also, a noreturn attribute would allow overriding a non-void class method with a noreturn one (e.g. the derived class is a sentinel object that forces an exception / termination upon calling that method), whereas you can't do that with a None return type because the signature would not match the base class's. T -- "Hi." "'Lo."
Jul 08 2017
On Saturday, 8 July 2017 at 20:27:11 UTC, H. S. Teoh wrote:Also, a noreturn attribute would allow overriding a non-void class method with a noreturn one (e.g. the derived class is a sentinel object that forces an exception / termination upon calling that method), whereas you can't do that with a None return type because the signature would not match the base class's. TThe reverse is also true, and more disastrous!
Jul 13 2017
On Saturday, 8 July 2017 at 12:17:57 UTC, Andrei Alexandrescu wrote:disable property None init();You meant static here I guess.The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return.Cheat: https://is.gd/pf25nP Works because of NRVO I guess. This particular one is countered by also adding a disabled destructor.
Jul 08 2017
On Saturday, 8 July 2017 at 21:03:57 UTC, Vladimir Panteleev wrote:This particular one is countered by also adding a disabled destructor.Oops, never mind that - this makes the function uncallable.
Jul 08 2017
On 07/08/2017 05:03 PM, Vladimir Panteleev wrote:On Saturday, 8 July 2017 at 12:17:57 UTC, Andrei Alexandrescu wrote:Eh, interesting. Indeed this doesn't compile anymore: struct None { disable this(); disable this(this); disable ~this(); disable static property None init(); } None fun() { None none = void; return none; } void main() { fun(); } The type None would then go in object.d. The compiler detects the pattern and make None implicitly convertible to anything so people can write things like: int x = y ? 100 / y : fun(); Without the conversion, None is a less useful artifact. Andreidisable property None init();You meant static here I guess.The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return.Cheat: https://is.gd/pf25nP Works because of NRVO I guess. This particular one is countered by also adding a disabled destructor.
Jul 08 2017
On Saturday, 8 July 2017 at 12:17:57 UTC, Andrei Alexandrescu wrote:struct None { disable this(); disable this(this); disable property None init(); } None ThisFunctionExits(); The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return.That's a lot more complex (for the compiler and to explain) than using a simple magic noreturn attribute. Agreed that this is rarely needed but sometimes nice to have. Far from being important though ;).
Jul 08 2017
On 7/8/2017 4:36 PM, Martin Nowak wrote:That's a lot more complex (for the compiler and to explain) than using a simple magic noreturn attribute. Agreed that this is rarely needed but sometimes nice to have. Far from being important though ;).We have types that cannot be named (Voldemort types), types that have no type (void), I suppose that types that cannot exist will fill out the edge cases of the menagerie. I assume there is a standard jargon for this - does anyone know Type Theory? Are there any other interesting uses for a type that cannot exist?
Jul 08 2017
On Sunday, 9 July 2017 at 00:16:50 UTC, Walter Bright wrote:I assume there is a standard jargon for this - does anyone know Type Theory?I'm afraid it's perpendicular to type theory - every type including Nothing can be returned. Most simple solution - just to remove ' ' and call this type 'noreturn'. noreturn functionFoo(bla-bla-bla);
Jul 08 2017
On Sunday, 9 July 2017 at 00:16:50 UTC, Walter Bright wrote:We have types that cannot be named (Voldemort types), types that have no type (void), I suppose that types that cannot exist will fill out the edge cases of the menagerie. I assume there is a standard jargon for this - does anyone know Type Theory? Are there any other interesting uses for a type that cannot exist?In pure functional languages, that's what "bottom" or Haskell's Void is. In Curry–Howard "programs are proofs" theory, a type is a proposition and an instance is a proof. A type with no instance is a proposition that can't be proved. https://codewords.recurse.com/issues/one/type-systems-and-logic I'm not sure how much impact this has on everyday D programming, but hey, it's a thing.
Jul 08 2017
On 7/8/2017 7:01 PM, sarn wrote:On Sunday, 9 July 2017 at 00:16:50 UTC, Walter Bright wrote:Thanks, it looks like we won't get any help from type theory. We're on our own :-) (D already has a `void` type, so can't use Haskell's word.)We have types that cannot be named (Voldemort types), types that have no type (void), I suppose that types that cannot exist will fill out the edge cases of the menagerie. I assume there is a standard jargon for this - does anyone know Type Theory? Are there any other interesting uses for a type that cannot exist?In pure functional languages, that's what "bottom" or Haskell's Void is. In Curry–Howard "programs are proofs" theory, a type is a proposition and an instance is a proof. A type with no instance is a proposition that can't be proved. https://codewords.recurse.com/issues/one/type-systems-and-logic I'm not sure how much impact this has on everyday D programming, but hey, it's a thing.
Jul 08 2017
On Sunday, 9 July 2017 at 02:25:50 UTC, Walter Bright wrote:(D already has a `void` type, so can't use Haskell's word.)Just so we are all on the same page, from a type-theory perspective void is a unit type (it has 1 value), not an uninhabited type (it has no values, i.e. Haskell's _|_ (bottom) type). A function with a return type of unit means "this function returns no useful information", because the type only has one possible value anyway. A function with a return type of bottom means "this function can never return", because there is no value of type bottom that could be returned. All that can be done is for the function to diverge (throwing an exception, ending the program, looping forever, etc.). We sort of have this already with `assert(0)`. The compiler knows that no execution can take place after an `assert(0)` is encountered (in other words, it knows that the function diverges). We just don't have a corresponding type to represent this (Rust uses ! but if I remember correctly it's not quite a first class type). If we wanted to be cute we could use `typeof()` to represent this type as there is no value you can give to typeof such that it returns the bottom type. It also avoids having to come up with some special symbol or name for it.
Jul 08 2017
On Sunday, 9 July 2017 at 04:23:15 UTC, Meta wrote:On Sunday, 9 July 2017 at 02:25:50 UTC, Walter Bright wrote:In C++/D terms, the unit type could be expressed as `enum Unit { unit }` while the bottom type would be `enum Bottom {}` (although only conceptually because in C++/D you can still do `Bottom b;`).(D already has a `void` type, so can't use Haskell's word.)Just so we are all on the same page, from a type-theory perspective void is a unit type (it has 1 value), not an uninhabited type (it has no values, i.e. Haskell's _|_ (bottom) type). A function with a return type of unit means "this function returns no useful information", because the type only has one possible value anyway. A function with a return type of bottom means "this function can never return", because there is no value of type bottom that could be returned. All that can be done is for the function to diverge (throwing an exception, ending the program, looping forever, etc.). We sort of have this already with `assert(0)`. The compiler knows that no execution can take place after an `assert(0)` is encountered (in other words, it knows that the function diverges). We just don't have a corresponding type to represent this (Rust uses ! but if I remember correctly it's not quite a first class type). If we wanted to be cute we could use `typeof()` to represent this type as there is no value you can give to typeof such that it returns the bottom type. It also avoids having to come up with some special symbol or name for it.
Jul 08 2017
On 7/8/2017 9:32 PM, Meta wrote:On Sunday, 9 July 2017 at 04:23:15 UTC, Meta wrote:I read that a Void function in Haskell does not return. Is that incorrect? I'm not sure how Void relates to _|_ https://en.wikipedia.org/wiki/Bottom_type#In_programming_languages says Haskell does not support empty (i.e. bottom) types.On Sunday, 9 July 2017 at 02:25:50 UTC, Walter Bright wrote:(D already has a `void` type, so can't use Haskell's word.)Just so we are all on the same page, from a type-theory perspective void is a unit type (it has 1 value), not an uninhabited type (it has no values, i.e. Haskell's _|_ (bottom) type).In D, void is used to signify a function doesn't return a value, but it still returns. It also means untyped data, and no initializer.A function with a return type of unit means "this function returns no useful information", because the type only has one possible value anyway. A function with a return type of bottom means "this function can never return", because there is no value of type bottom that could be returned. All that can be done is for the function to diverge (throwing an exception, ending the program, looping forever, etc.). We sort of have this already with `assert(0)`. The compiler knows that no execution can take place after an `assert(0)` is encountered (in other words, it knows that the function diverges). We just don't have a corresponding type to represent this (Rust uses ! but if I remember correctly it's not quite a first class type). If we wanted to be cute we could use `typeof()` to represent this type as there is no value you can give to typeof such that it returns the bottom type. It also avoids having to come up with some special symbol or name for it.In C++/D terms, the unit type could be expressed as `enum Unit { unit }` while the bottom type would be `enum Bottom {}` (although only conceptually because in C++/D you can still do `Bottom b;`).
Jul 08 2017
On 09.07.2017 07:44, Walter Bright wrote:On 7/8/2017 9:32 PM, Meta wrote:It is indeed incorrect, but this has little relevance for D. In Haskell, non-termination and exceptions are values that you can store in a variable (due to lazy evaluation). A function can return non-termination or exceptions non-evaluated. When those values are finally lazily evaluated, you get actual non-termination or a thrown exception. This is why Haskell has no empty type. If evaluation in Haskell was strict (as D's is), a function with return type Void would not be able to return. (BTW: it makes more sense to return a polymorphic value, i.e. x :: t instead of using the Void type, because Haskell does not have subtyping. This still implies that x does not contain a 'real' value, because we can instantiate t with Void.)On Sunday, 9 July 2017 at 04:23:15 UTC, Meta wrote:I read that a Void function in Haskell does not return. Is that incorrect?On Sunday, 9 July 2017 at 02:25:50 UTC, Walter Bright wrote:(D already has a `void` type, so can't use Haskell's word.)Just so we are all on the same page, from a type-theory perspective void is a unit type (it has 1 value), not an uninhabited type (it has no values, i.e. Haskell's _|_ (bottom) type).I'm not sure how Void relates to _|_ https://en.wikipedia.org/wiki/Bottom_type#In_programming_languages says Haskell does not support empty (i.e. bottom) types. ...
Jul 09 2017
On 7/9/2017 3:48 AM, Timon Gehr wrote:It is indeed incorrect, [...]Thanks!
Jul 09 2017
On 7/8/2017 9:23 PM, Meta wrote:On Sunday, 9 July 2017 at 02:25:50 UTC, Walter Bright wrote:Thanks for the explanation.(D already has a `void` type, so can't use Haskell's word.)Just so we are all on the same page, from a type-theory perspective void is a unit type (it has 1 value), not an uninhabited type (it has no values, i.e. Haskell's _|_ (bottom) type). A function with a return type of unit means "this function returns no useful information", because the type only has one possible value anyway. A function with a return type of bottom means "this function can never return", because there is no value of type bottom that could be returned. All that can be done is for the function to diverge (throwing an exception, ending the program, looping forever, etc.).We sort of have this already with `assert(0)`. The compiler knows that no execution can take place after an `assert(0)` is encountered (in other words, it knows that the function diverges). We just don't have a corresponding type to represent this (Rust uses ! but if I remember correctly it's not quite a first class type). If we wanted to be cute we could use `typeof()` to represent this type as there is no value you can give to typeof such that it returns the bottom type. It also avoids having to come up with some special symbol or name for it.That is indeed an interesting idea. Thanks! (This thread is turning out to be more productive than I anticipated.)
Jul 08 2017
On 7/9/17 1:36 AM, Walter Bright wrote:We should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null). -- AndreiIf we wanted to be cute we could use `typeof()` to represent this type as there is no value you can give to typeof such that it returns the bottom type. It also avoids having to come up with some special symbol or name for it.That is indeed an interesting idea. Thanks!
Jul 09 2017
On Sunday, 9 July 2017 at 13:13:28 UTC, Andrei Alexandrescu wrote:On 7/9/17 1:36 AM, Walter Bright wrote:I agree with is approach. It does not add a magic Type :)We should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null). -- AndreiIf we wanted to be cute we could use `typeof()` to represent this type as there is no value you can give to typeof such that it returns the bottom type. It also avoids having to come up with some special symbol or name for it.That is indeed an interesting idea. Thanks!
Jul 09 2017
On 7/9/2017 6:13 AM, Andrei Alexandrescu wrote:We should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null).I had forgotten about the typeof(null) thing. You're right. But there are some issues. What do we do with: typeof(assert(0))* p; ? What does that mean?
Jul 09 2017
On Sunday, 9 July 2017 at 19:12:45 UTC, Walter Bright wrote:On 7/9/2017 6:13 AM, Andrei Alexandrescu wrote:Not valid. Cannot take a pointer of typeof(assert(0)).We should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null).I had forgotten about the typeof(null) thing. You're right. But there are some issues. What do we do with: typeof(assert(0))* p; ? What does that mean?
Jul 09 2017
On 07/09/2017 03:12 PM, Walter Bright wrote:On 7/9/2017 6:13 AM, Andrei Alexandrescu wrote:That would be a pointer that may only be null - a consequence of the typeof(assert(0)) being uninstantiable. Generally I'm not too worried about constructs like typeof(assert(0))[], typeof(assert(0))*, use in templates etc - we don't need to "design" these cases, their behavior flows from the properties of typeof(assert(0)) itself. Similarly, I don't recall ever there being a problem with typeof(null)*, typeof(null)[], people complaining they passed typeof(null) to a template where it did bad things, etc. AndreiWe should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null).I had forgotten about the typeof(null) thing. You're right. But there are some issues. What do we do with: typeof(assert(0))* p; ? What does that mean?
Jul 09 2017
On Sunday, 9 July 2017 at 22:28:50 UTC, Andrei Alexandrescu wrote:On 07/09/2017 03:12 PM, Walter Bright wrote:`typeof(null)` actually has one valid value and doesn't crash the program when when you try to create an instance of it. We should not treat this the same as `typeof(null)`.On 7/9/2017 6:13 AM, Andrei Alexandrescu wrote:That would be a pointer that may only be null - a consequence of the typeof(assert(0)) being uninstantiable. Generally I'm not too worried about constructs like typeof(assert(0))[], typeof(assert(0))*, use in templates etc - we don't need to "design" these cases, their behavior flows from the properties of typeof(assert(0)) itself. Similarly, I don't recall ever there being a problem with typeof(null)*, typeof(null)[], people complaining they passed typeof(null) to a template where it did bad things, etc. AndreiWe should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null).I had forgotten about the typeof(null) thing. You're right. But there are some issues. What do we do with: typeof(assert(0))* p; ? What does that mean?
Jul 09 2017
On 07/09/2017 07:34 PM, Meta wrote:On Sunday, 9 July 2017 at 22:28:50 UTC, Andrei Alexandrescu wrote:Agreed (with the mention it's not in contradiction with the above). -- AndreiOn 07/09/2017 03:12 PM, Walter Bright wrote:`typeof(null)` actually has one valid value and doesn't crash the program when when you try to create an instance of it. We should not treat this the same as `typeof(null)`.On 7/9/2017 6:13 AM, Andrei Alexandrescu wrote:That would be a pointer that may only be null - a consequence of the typeof(assert(0)) being uninstantiable. Generally I'm not too worried about constructs like typeof(assert(0))[], typeof(assert(0))*, use in templates etc - we don't need to "design" these cases, their behavior flows from the properties of typeof(assert(0)) itself. Similarly, I don't recall ever there being a problem with typeof(null)*, typeof(null)[], people complaining they passed typeof(null) to a template where it did bad things, etc. AndreiWe should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null).I had forgotten about the typeof(null) thing. You're right. But there are some issues. What do we do with: typeof(assert(0))* p; ? What does that mean?
Jul 09 2017
On Monday, 10 July 2017 at 00:34:54 UTC, Andrei Alexandrescu wrote:That was in response to:`typeof(null)` actually has one valid value and doesn't crash the program when when you try to create an instance of it. We should not treat this the same as `typeof(null)`.Agreed (with the mention it's not in contradiction with the above). -- AndreiMy point was that I don't agree with the analogy as there really isn't any similarity between typeof(assert(0)) and typeof(null). There are no problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom. Although now that you mention it, there might be some interesting uses for a pointer/array that we statically know can only be null (none that I can think of OTOH though). I still think we should err on the side of disabling stuff like `immutable Bottom`, `Bottom*`, `Nullable!Bottom`, etc. until we fully understand the ramifications. We also cannot allow struct/class members of the type Bottom for obvious reasons. Interestingly enough, unions would conceptually be okay, and you would statically know that a union such as `union IntOrBottom { int n; Bottom b; }` can only ever contain an int. Therefore, IntOrBottom could safely be aliased away to just int (syntactic differences between working with a union vs. working with an int aside). We also need to disallow `enum Sneaky: Bottom { val }` and `enum: Bottom { val }`. I don't know how enums are implemented in the compiler so it may be a non-issue. `enum Okay: Bottom {}` is fine and Okay could just alias itself away (but we should still disable it to prevent confusion). `enum: Bottom {}` makes no sense so we should prevent that as well.Similarly, I don't recall ever there being a problem with typeof(null)*, typeof(null)[], people complaining they passed typeof(null) to a template where it did bad things, etc.
Jul 10 2017
On 10.07.2017 18:23, Meta wrote:... problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.Bottom is just a regular type.
Jul 11 2017
On Tuesday, 11 July 2017 at 08:29:12 UTC, Timon Gehr wrote:On 10.07.2017 18:23, Meta wrote:It's a regular type with unusual behaviour due to it being uninhabited.... problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.Bottom is just a regular type.
Jul 11 2017
On 11.07.2017 19:27, Meta wrote:On Tuesday, 11 July 2017 at 08:29:12 UTC, Timon Gehr wrote:All types are unusual. That does not mean they are not all types.On 10.07.2017 18:23, Meta wrote:It's a regular type with unusual behaviour due to it being uninhabited.... problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.Bottom is just a regular type.
Jul 12 2017
On 7/11/17 4:29 AM, Timon Gehr wrote:On 10.07.2017 18:23, Meta wrote:Wouldn't the fact that it's a subtype of all other types make it a bit more peculiar? -- Andrei... problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.Bottom is just a regular type.
Jul 11 2017
On 12.07.2017 03:50, Andrei Alexandrescu wrote:On 7/11/17 4:29 AM, Timon Gehr wrote:Every type is peculiar. That's essentially the point of having types. There is not really a reason to invent a peculiarity ordering and then add additional special casing for types deemed more peculiar. (I.e., creating different types of types based on an informal judgment of peculiarity.) In D, subtyping is messy anyway, as you cannot have a subtyping relationship between values with different memory layout. Hence in D, Bottom would not actually be a subtype of all other types. In a very expressive type system, there are many more, functionally different types that are uninhabited, not just Bottom.On 10.07.2017 18:23, Meta wrote:Wouldn't the fact that it's a subtype of all other types make it a bit more peculiar? -- Andrei... problems with stuff like typeof(null)* etc. because for the most part it's just a regular type, unlike Bottom.Bottom is just a regular type.
Jul 12 2017
Let's call the bottom type 'B' for the moment, for convenience. If we think of it like a floating point NaN, then any type construction of B yields a B: const(B) -> B B* -> B B[] -> B X[B] -> B B[X] -> B Since B cannot have a value, any expression that forms a B can be replaced with an assert(0). B foo(); // declaration foo() -> foo(); assert(0); cast(B)exp -> exp; assert(0); B b; -> assert(0); Given a tuple of types: alias T = tuple(B,X); is T equivalent to which of: B (1) tuple(X) (2) tuple(Y,X) (3) tuple(B,X) (4) ? I'm leaning toward (4) as making the most sense. struct S { T t; } should then yield an error, while: struct S { T[1..1] t; // t is of type X } would not.
Jul 09 2017
On 10.07.2017 04:44, Walter Bright wrote:Let's call the bottom type 'B' for the moment, for convenience. If we think of it like a floating point NaN,It's not. It's an empty type.then any type construction of B yields a B: const(B) -> BI don't see why to add this special case.B* -> B B[] -> B X[B] -> B B[X] -> B ...This would be wrong. All of the types on the left have valid values, they are not isomorphic to B.Since B cannot have a value, any expression that forms a B can be replaced with an assert(0). B foo(); // declaration foo() -> foo(); assert(0);Yes.cast(B)exp -> exp; assert(0); B b; -> assert(0);Those wouldn't compile. (Nothing can be cast to bottom, there is no default initializer that the variable declaration can use.)Given a tuple of types: alias T = tuple(B,X); is T equivalent to which of: B (1) tuple(X) (2) tuple(Y,X) (3) tuple(B,X) (4) ? I'm leaning toward (4) as making the most sense. ...It's the only one that makes any sense.struct S { T t; } should then yield an error, while: struct S { T[1..1] t; // t is of type X } would not.I guess you meant T[1..2].
Jul 11 2017
On 7/9/2017 6:13 AM, Andrei Alexandrescu wrote:We should use typeof(assert(0)) for Bottom.That also leaves the door open for: alias noreturn = typeof(assert(0));
Jul 09 2017
On Sunday, 9 July 2017 at 19:14:37 UTC, Walter Bright wrote:On 7/9/2017 6:13 AM, Andrei Alexandrescu wrote:I would really prefer noreturn (or noreturn_t) to be the default appellation for such a type. typeof(assert(0)) is way uglier, while noreturn is a lot more intuitive and explicit.We should use typeof(assert(0)) for Bottom.That also leaves the door open for: alias noreturn = typeof(assert(0));
Jul 10 2017
On 07/08/2017 10:25 PM, Walter Bright wrote:On 7/8/2017 7:01 PM, sarn wrote:How does the information provided lead to such a conclusion? There's established theory and practice on that. https://en.wikipedia.org/wiki/Bottom_type The "Bottom" type (bottom of the type hierarchy lattice) is what's needed. If "Object" is the total set i.e. the top of the lattice i.e. the type that is so general all types are a subset of it, then "Bottom" is the type that is a subtype of all types and is so particular it can't be even instantiated. It implicitly converts to everything because it's a subtype of everything. Obviously conversion doesn't need to be honored because the function never returns. AndreiOn Sunday, 9 July 2017 at 00:16:50 UTC, Walter Bright wrote:Thanks, it looks like we won't get any help from type theory. We're on our own :-)We have types that cannot be named (Voldemort types), types that have no type (void), I suppose that types that cannot exist will fill out the edge cases of the menagerie. I assume there is a standard jargon for this - does anyone know Type Theory? Are there any other interesting uses for a type that cannot exist?In pure functional languages, that's what "bottom" or Haskell's Void is. In Curry–Howard "programs are proofs" theory, a type is a proposition and an instance is a proof. A type with no instance is a proposition that can't be proved. https://codewords.recurse.com/issues/one/type-systems-and-logic I'm not sure how much impact this has on everyday D programming, but hey, it's a thing.
Jul 08 2017
On Sun, Jul 09, 2017 at 02:01:26AM +0000, sarn via Digitalmars-d wrote:On Sunday, 9 July 2017 at 00:16:50 UTC, Walter Bright wrote:Technically, the *type* itself exist, it's just that you cannot create any instances of it. :-P From the POV of types as sets of possible values, it's a type that corresponds with an empty set. However, then we cannot distinguish between void (which is also by definition a type that has no instances) and noreturn. I'm not sure how to interpret noreturn in this framework... perhaps a set that doesn't exist? :-P We could call it a RussellsParadox set. :-DWe have types that cannot be named (Voldemort types), types that have no type (void), I suppose that types that cannot exist will fill out the edge cases of the menagerie. I assume there is a standard jargon for this - does anyone know Type Theory? Are there any other interesting uses for a type that cannot exist?In pure functional languages, that's what "bottom" or Haskell's Void is.[...] Are you sure? IIRC, it's legal to return bottom; it just signifies that the value is invalid, and that any operation on it will also result in bottom. Sortof like NaN. That's not the same thing as "this function never returns". T -- The computer is only a tool. Unfortunately, so is the user. -- Armaphine, K5
Jul 08 2017
On 07/08/2017 08:17 AM, Andrei Alexandrescu wrote:On 7/8/17 6:15 AM, Walter Bright wrote:Pro: - Having the indication "this doesn't return" syntactically be the return type makes a lot of sense. I like that a lot. (Though I'd have no objection to Walter's original suggestion either). - It's one less built-in attribute to scare people away with objections of "too many attributes"! (And admittedly, idiomatic D can end up with lots of attribtes all over the place.) Con: - Inferring that a type is meant to indicate "functions 'returning' this do not return", strikes me as very round-about, hackish, unintuitive and just unclean. I really think this is one case where a dedicated compiler-understood symbol is not only fully justified but also much cleaner. "NoReturn" (or whatever it'll be called) a special compiler-understood "type", not something inferred from the detailed of a library-provided type. In any case, I'm definitely onboard with the idea of having SOME standard way to tell the compiler "this does not return". That would be nice to have.Has anyone a better idea? Does anyone want to write a DIP for this?An attribute is fine. A more PL-minded possibility is to return a specific type: struct None { disable this(); disable this(this); disable property None init(); } None ThisFunctionExits(); The compiler detects (without having anything hardwired about the particular type "None") that the type None is impossible to create and copy/move from a function, and therefore decrees the function will never return.
Jul 09 2017
On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:C compilers (and by extension C++ compilers) usually have an extension which allows a function to be marked as one that never returns. The point of this is it enables improved data flow analysis and better code being generated. Noreturn functions crop up in things like assert's and enforce's. DMD internally hardcodes a few functions it knows about that are noreturn, and the DMD optimizer and codegen take advantage of it. But when people write their own assert's and enforce's, this falls apart. While the programs will still work, they won't be as efficient as they could be. Having an noreturn attribute will take care of that: noreturn void ThisFunctionExits(); Yes, it's another builtin attribute and attributes are arguably a failure in language design. Has anyone a better idea? Does anyone want to write a DIP for this? Example: DMC uses a pragma to do it: void ThisFunctionExits(); #pragma noreturn(ThisFunctionExits); GCC uses an attribute: void ThisFunctionExits() __attribute__ ((__noreturn__)); VC uses: __declspec(noreturn) void ThisFunctionExits();Some questions... What kinds of control flow does this apply to? My guess is that you consider a function to be "no-return" so long as it never returns control directly back to the caller. The examples I can think of would be functions that prevent returning to the caller by calling exit, asserting, throwing or even just executing an infinite loop (expecting the program to exit via signal or some other means). Is this right? Another question I had was would the compiler provide control flow checking to make sure that the function does not return? Also, I can imagine some optimizations that can be done with this information, would you mind sharing the optimization(s) you had in mind?
Jul 08 2017
On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On Sunday, 9 July 2017 at 10:51:50 UTC, Daniel N wrote:On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Yes, this! -SteveOn Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On Sunday, 9 July 2017 at 10:59:43 UTC, Steven Schveighoffer wrote:On Sunday, 9 July 2017 at 10:51:50 UTC, Daniel N wrote:That certainly signals intention, but I still think that an attribute is a whole lot simpler than compiler voodoo like this or the "None" discussed earlier.On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Yes, this! -SteveOn Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On 7/9/17 7:07 AM, Nicholas Wilson wrote:On Sunday, 9 July 2017 at 10:59:43 UTC, Steven Schveighoffer wrote:This whole proposal is compiler voodoo :) Unlike a currently undefined attribute, assert(0) in the out contract currently is effective to mean no return. It also guarantees that if there is a return statement somewhere, it still doesn't return. Also, since the out contract is part of the signature, it will still be present in the case of separate compilation. The one disadvantage, is that -release removes contracts. So in this particular case, the out contract should remain. Not sure if it has been mentioned yet, but disable(return) could work as something that doesn't add any new attributes. -SteveOn Sunday, 9 July 2017 at 10:51:50 UTC, Daniel N wrote:That certainly signals intention, but I still think that an attribute is a whole lot simpler than compiler voodoo like this or the "None" discussed earlier.On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Yes, this!On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On Sunday, 9 July 2017 at 11:26:27 UTC, Steven Schveighoffer wrote:On 7/9/17 7:07 AM, Nicholas Wilson wrote:If the compiler is like a magician, adding an attribute is like conjuring a rabbit, adding the "None" would be like summoning Chthulu. I like the disable(return) but I still think a plain attribute is the simplest way to achieve the goals of: * documentation, is doesn't get much simpler than noreturn * optimisation, its already in LDC and GDC, just under a different name * compatibility between compilers, its as simple as an alias in a version statement per compiler.On Sunday, 9 July 2017 at 10:59:43 UTC, Steven Schveighoffer wrote:This whole proposal is compiler voodoo :) Unlike a currently undefined attribute, assert(0) in the out contract currently is effective to mean no return. It also guarantees that if there is a return statement somewhere, it still doesn't return. Also, since the out contract is part of the signature, it will still be present in the case of separate compilation. The one disadvantage, is that -release removes contracts. So in this particular case, the out contract should remain. Not sure if it has been mentioned yet, but disable(return) could work as something that doesn't add any new attributes. -SteveOn Sunday, 9 July 2017 at 10:51:50 UTC, Daniel N wrote:That certainly signals intention, but I still think that an attribute is a whole lot simpler than compiler voodoo like this or the "None" discussed earlier.On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Yes, this!On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On 7/9/17 7:26 AM, Steven Schveighoffer wrote:On 7/9/17 7:07 AM, Nicholas Wilson wrote:I wonder why there's so much attraction to exotic approaches when encoding "no return" with types has so much established theory and practice with it. -- AndreiOn Sunday, 9 July 2017 at 10:59:43 UTC, Steven Schveighoffer wrote:This whole proposal is compiler voodoo :) Unlike a currently undefined attribute, assert(0) in the out contract currently is effective to mean no return. It also guarantees that if there is a return statement somewhere, it still doesn't return. Also, since the out contract is part of the signature, it will still be present in the case of separate compilation. The one disadvantage, is that -release removes contracts. So in this particular case, the out contract should remain. Not sure if it has been mentioned yet, but disable(return) could work as something that doesn't add any new attributes.On Sunday, 9 July 2017 at 10:51:50 UTC, Daniel N wrote:That certainly signals intention, but I still think that an attribute is a whole lot simpler than compiler voodoo like this or the "None" discussed earlier.On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Yes, this!On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On 7/9/17 9:16 AM, Andrei Alexandrescu wrote:I wonder why there's so much attraction to exotic approaches when encoding "no return" with types has so much established theory and practice with it. -- AndreiYour definition of exotic differs from mine. I haven't seen another solution other than out { assert(0); } which semantically means "this function cannot return" in the current compiler, and pretty much all historical D compilers, is obvious to the reader that it does so, available in the prototype, and is there and ready for the compiler to optimize with. Literally we have to change nothing in the language or library, only how the optimizer handles it. Adding specialized attributes, esoteric types, etc, does not have this same level of compatibility. That is why I find this more attractive than any other suggestions so far. -Steve
Jul 09 2017
On 07/09/2017 10:10 AM, Steven Schveighoffer wrote:On 7/9/17 9:16 AM, Andrei Alexandrescu wrote:Mine is closest to: "strikingly, excitingly, or mysteriously different or unusual" :o) in https://www.merriam-webster.com/dictionary/exotic -- AndreiI wonder why there's so much attraction to exotic approaches when encoding "no return" with types has so much established theory and practice with it. -- AndreiYour definition of exotic differs from mine.
Jul 09 2017
On 07/09/2017 10:10 AM, Steven Schveighoffer wrote:I haven't seen another solution other than out { assert(0); }Your own comment takes it to the recycle bin:The one disadvantage, is that -release removes contracts. So in this particular case, the out contract should remain.That's already a hack on top of a hack. Andrei
Jul 09 2017
On Sunday, 9 July 2017 at 14:43:20 UTC, Andrei Alexandrescu wrote:On 07/09/2017 10:10 AM, Steven Schveighoffer wrote:An out contract should apply even when contracts are removed. The compiler can treat it as not returning even if contracts are removed. But the compiler could in this case leave the contract in as a stop gap in case the function does return. I fact it's fine if it doesn't. It will just be UB. It's no more of a hack than leaving assert(0) in release code. -SteveI haven't seen another solution other than out { assert(0); }Your own comment takes it to the recycle bin:The one disadvantage, is that -release removes contracts. So in this particular case, the out contract should remain.That's already a hack on top of a hack.
Jul 09 2017
On 07/09/2017 12:19 PM, Steven Schveighoffer wrote:It's no more of a hack than leaving assert(0) in release code.I wouldn't argue that. I do argue it's a hack compared to the principled solution of a bottom type. -- Andrei
Jul 09 2017
On Sunday, 9 July 2017 at 18:01:08 UTC, Andrei Alexandrescu wrote:On 07/09/2017 12:19 PM, Steven Schveighoffer wrote:Adding a new builtin-type would also be okay. But a compiler-recognized custom structs is quite costly and brings with it a much higher complexity and more importantly a higher compile-time cost. It would also require yet another symbol in object.d which is too big as it is.It's no more of a hack than leaving assert(0) in release code.I wouldn't argue that. I do argue it's a hack compared to the principled solution of a bottom type. -- Andrei
Jul 09 2017
On Sunday, 9 July 2017 at 18:01:08 UTC, Andrei Alexandrescu wrote:On 07/09/2017 12:19 PM, Steven Schveighoffer wrote:I thought some more about the ramifications of having a Bottom type in D. Having a special type like this interacts badly with most aspects of D's generic programming capabilities, even on the simplest level. At the least we would probably have to create special cases everywhere in the compiler and/or libraries that check if the type we're working with is the bottom type. A few simple cases I can think of off the top of my head: alias Bottom = typeof(assert(0)); //for convenience Bottom* pb; //Must be statically disallowed as this makes no sense Bottom[] ab; //ditto cast(Bottom)1; //ditto struct TupleLike(T...) { T fields; } //What now? Do we allow this type to be instantiated but never created, //as it contains Bottom? Or do we allow it to be instantiated and created, //but the program crashes at runtime when it actually attempts to create a //value of type Bottom? TupleLike!(string, int, Bottom) t; //Likewise. Either user code will have to add `if (!is(T == Bottom))` //every time they define a wrapper struct like this, or an error //would be generated from inside the aggregate the declares a `T t` //member. The former is tedious, the latter defeats the purpose //of template guards in the first place alias Bad = Nullable!Bottom; And I'm sure there are many more. We could make passing Bottom as a template argument an error, which seems reasonable to me (this is the route Rust goes, I think). Here's a few good links: https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#the-never-type--that-never-returns https://www.reddit.com/r/rust/comments/3j22vx/what_is_the_meaning_of_as_a_return_type/ https://github.com/rust-lang/rfcs/pull/1216 There's a discussion in that Reddit thread about some of the advantages/disadvantages of making Bottom a first-class type. Also a point of interest is that by adding a Bottom type to D and with a bit of fiddling we can *almost* implement `assert(0)` in library code as opposed to in the compiler: void assert(T)(lazy T cond, lazy string msg = "") if (is(typeof(cast(bool)cond)) { if (is(T == int) && cond == 0)) { //Compiles recognizes that a value of type Bottom //is being created and inserts a HLT instruction //(Or whatever it is that assert(0) does nowadays) Bottom impossible; } if (cond) return; if (msg.length) { import std.stdio; stderr.writeln(msg); } assert(0); } I say almost because there's no way in user code to detect the specific form `assert(0)` without making changes to how we call it. A user could unintentionally halt the program by doing something like `assert(m - n, "m and n should not be the same")`. It could be done with templates but it'd break user code: //`assert(0)` must now be called like `assert!0` Bottom assert(int n: 0)(lazy string msg = "") { if (msg.length) { import std.stdio; stderr.writeln(msg); } Bottom impossible; }It's no more of a hack than leaving assert(0) in release code.I wouldn't argue that. I do argue it's a hack compared to the principled solution of a bottom type. -- Andrei
Jul 09 2017
On Sunday, 9 July 2017 at 19:30:25 UTC, Meta wrote:I thought some more about the ramifications of having a Bottom type in D. Having a special type like this interacts badly with most aspects of D's generic programming capabilities, even on the simplest level. At the least we would probably have to create special cases everywhere in the compiler and/or libraries that check if the type we're working with is the bottom type. A few simple cases I can think of off the top of my head: alias Bottom = typeof(assert(0)); //for convenience Bottom* pb; //Must be statically disallowed as this makes no sense Bottom[] ab; //ditto cast(Bottom)1; //dittoAnother case that we should probably just statically disallow: alias ImmutableBottom = immutable Bottom; //Ditto for shared, const, etc. This obviously doesn't make any sense anyway.
Jul 09 2017
On 7/9/2017 1:24 PM, Meta wrote:Another case that we should probably just statically disallow: alias ImmutableBottom = immutable Bottom; //Ditto for shared, const, etc. This obviously doesn't make any sense anyway.Wouldn't `immutable(Bottom)` simply resolve to `Bottom`?
Jul 09 2017
On Sunday, 9 July 2017 at 21:32:31 UTC, Walter Bright wrote:On 7/9/2017 1:24 PM, Meta wrote:I was thinking about that (and in fact, making Bottom a "viral" type in the same way that NaN is a viral value), but it's not really worth it and we might as well make it an error. It can't occur in any template code if we disallow passing Bottom as a template argument, e.g., the following would fail: alias Pointificate(T) = T*; alias Immutable(T) = immutable T; //Error: cannot pass Bottom as a template type argument alias PointerToBottom = Pointificate!Bottom; alias ImmutableBottom = Immutable!Bottom; So the only place left where we could make a modified Bottom type would be doing `alias Immutable Bottom = immutable Bottom` and the like. It doesn't really matter either way, I don't think, but we might as well keeps things streamlined and just disallow everything to do with Bottom that we don't like. Unless there's a compelling reason to be able to apply immutable, shared, etc. to Bottom (and I don't see how there can be as you can never have a value of type Bottom that could be immutable, shared, etc. anyway) we should just disallow it. Some functional languages do allow you to create things like `Either!(T, Bottom)` (from the Reddit thread: "Sometimes you have a generic procedure over a sum type and you want to pass it a single thing: `Either a Bottom` is isomorphic to a.") but I don't see a reason for us to ever need to do that.Another case that we should probably just statically disallow: alias ImmutableBottom = immutable Bottom; //Ditto for shared, const, etc. This obviously doesn't make any sense anyway.Wouldn't `immutable(Bottom)` simply resolve to `Bottom`?
Jul 09 2017
On 09.07.2017 23:45, Meta wrote:... Another case that we should probably just statically disallow: ... > This obviously doesn't make any sense anyway ... > I don't see a reason for us to ever need to do thatSorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
Jul 12 2017
On Wednesday, 12 July 2017 at 09:32:32 UTC, Timon Gehr wrote:On 09.07.2017 23:45, Meta wrote:D is not ML or Haskell or Idris. Rust has trod this ground before us and they saw it prudent to not make ! a first-class type. I think this is both a concession to usability and an acceptable compromise for a systems programming language.... Another case that we should probably just statically disallow: ... > This obviously doesn't make any sense anyway ... > I don't see a reason for us to ever need to do thatSorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
Jul 12 2017
On Wednesday, 12 July 2017 at 13:22:46 UTC, Meta wrote:D is not ML or Haskell or Idris. Rust has trod this ground before us and they saw it prudent to not make ! a first-class type.Only to turn around after they realized their mistake: https://doc.rust-lang.org/stable/book/second-edition/ch19-04-advanced-types.html#the-never-type--that-never-returns
Jul 19 2017
On Wednesday, 19 July 2017 at 11:02:07 UTC, Marc Schütz wrote:On Wednesday, 12 July 2017 at 13:22:46 UTC, Meta wrote:That page doesn't say anything about passing ! as a type to generic functions (what I'm referring to), but it seems the following code does compile on Rust nightly with the fn test<T>() {} fn main() { test::<!>(); } So it seems my Rust knowledge is about a year out of date.D is not ML or Haskell or Idris. Rust has trod this ground before us and they saw it prudent to not make ! a first-class type.Only to turn around after they realized their mistake: https://doc.rust-lang.org/stable/book/second-edition/ch19-04-advanced-types.html#the-never-type--that-never-returns
Jul 19 2017
On 07/12/2017 05:32 AM, Timon Gehr wrote:On 09.07.2017 23:45, Meta wrote:Timon, I think you're very well positioned to author a DIP on this. Getting through acceptance on your static foreach DIP seems to not require a lot of extra work.... Another case that we should probably just statically disallow: ... > This obviously doesn't make any sense anyway ... > I don't see a reason for us to ever need to do thatSorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.Every type is peculiar. That's essentially the point of having types. There is not really a reason to invent a peculiarity ordering and then add additional special casing for types deemed more peculiar. (I.e., creating different types of types based on an informal judgment of peculiarity.)I seem to recall Haskell calls those "kinds".In D, subtyping is messy anyway, as you cannot have a subtyping relationship between values with different memory layout. Hence in D, Bottom would not actually be a subtype of all other types.It's a point, and it would make the implementation easier, but it would be a departure from theory. Also it makes user code a tad more awkward. Consider: typeof(assert(0)) abort(const(char)[] message); ... int fun() { int x; ... return x != 0 ? 1024 / x : abort("Error: calculation went awry."); } I guess such expressions can be rewritten into separate statements: if (x != 0) return 1024 / x; abort("Error: calculation went awry."); and then the compiler figures there's no need for a return following the call to abort. Perhaps a solid plan is to start with a DIP that does not introduce conversion and then experiment with the result for a while. What do you think? Andrei
Jul 12 2017
On Wednesday, 12 July 2017 at 14:23:15 UTC, Andrei Alexandrescu wrote:On 07/12/2017 05:32 AM, Timon Gehr wrote:I might do that, however there are a couple of open questions (see below).On 09.07.2017 23:45, Meta wrote:Timon, I think you're very well positioned to author a DIP on this. Getting through acceptance on your static foreach DIP seems to not require a lot of extra work. ...... Another case that we should probably just statically disallow: ... > This obviously doesn't make any sense anyway ... > I don't see a reason for us to ever need to do thatSorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.Indeed, but the separation of types and kinds has no point. See: https://ghc.haskell.org/trac/ghc/wiki/DependentHaskell It's perfectly fine to have a type of types which is its own type, especially if you allow non-termination.Every type is peculiar. That's essentially the point of having types. There is not really a reason to invent a peculiarity ordering and then add additional special casing for types deemed more peculiar. (I.e., creating different types of types based on an informal judgment of peculiarity.)I seem to recall Haskell calls those "kinds". ...I'm saying the D notion of subtyping is a bit messy because memory layout matters and subtyping and implicit conversion are not the same thing. Anyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype. (Also, a language does not have to support subtyping to have an empty type.) typeof(assert(0))* and typeof(assert(0))[] will hence be subtypes of all other pointer and array types respectively. An issue is that we already have typeof(null). typeof(null) and typeof(assert(0))* are two ways to specify almost the same thing. One question is whether typeof(assert(0))* and typeof(null) should be the same, or if the former should not implicitly convert to class references. I have also argued in the past that there should be a separate typeof([]). This role would now be filled by typeof(assert(0))[]. However, changing the type of '[]' may break code.In D, subtyping is messy anyway, as you cannot have a subtyping relationship between values with different memory layout. Hence in D, Bottom would not actually be a subtype of all other types.It's a point, and it would make the implementation easier, but it would be a departure from theory. Also it makes user code a tad more awkward.Consider: typeof(assert(0)) abort(const(char)[] message); ... int fun() { int x; ... return x != 0 ? 1024 / x : abort("Error: calculation went awry."); } I guess such expressions can be rewritten into separate statements: if (x != 0) return 1024 / x; abort("Error: calculation went awry."); and then the compiler figures there's no need for a return following the call to abort. ...This code compiles and runs: int x; ... return x != 0 ? 1024 : (delegate int(){ assert(0,"Error: calculation went awry."); })();Perhaps a solid plan is to start with a DIP that does not introduce conversion and then experiment with the result for a while. What do you think? ...I think the DIP should introduce conversion from the start, as conversion is easy to support. It is simple to support in the front end and the code gen for it is literally to emit nothing.
Jul 13 2017
On Thursday, 13 July 2017 at 17:25:18 UTC, Timon Gehr wrote:Anyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype. ...(Actually, there are some complications like the .sizeof property. Anyway, it is clear what the semantics of Bottom are, no matter whether it is subtyping or implicit conversion.)
Jul 13 2017
On 7/13/17 2:37 PM, Timon Gehr wrote:On Thursday, 13 July 2017 at 17:25:18 UTC, Timon Gehr wrote:I wonder if sizeof could be made size_t.max. -- AndreiAnyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype. ...(Actually, there are some complications like the .sizeof property. Anyway, it is clear what the semantics of Bottom are, no matter whether it is subtyping or implicit conversion.)
Jul 13 2017
On 7/13/2017 5:18 PM, Andrei Alexandrescu wrote:On 7/13/17 2:37 PM, Timon Gehr wrote:I thought bottom.sizeof would be 0.On Thursday, 13 July 2017 at 17:25:18 UTC, Timon Gehr wrote:I wonder if sizeof could be made size_t.max. -- AndreiAnyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype. ...(Actually, there are some complications like the .sizeof property. Anyway, it is clear what the semantics of Bottom are, no matter whether it is subtyping or implicit conversion.)
Jul 15 2017
On 16.07.2017 05:30, Walter Bright wrote:On 7/13/2017 5:18 PM, Andrei Alexandrescu wrote:0 is the obvious size for the unit type (the type with a single value, in D this is for example void[0]), as in: struct S{ T x; void[0] nothing; } static assert(S.sizeof == T.sizeof); on the other hand struct S{ T x; Bottom everything; } turns the entire struct into an empty type. It is therefore most natural to say that Bottom.sizeof == ∞. (It's the only choice for which S.sizeof == Bottom.sizeof.) Another way to think about it: If something of type A* converts to something of type B* without problems, then one would expect B.sizeof <= A.sizeof. This would imply that Bottom.sizeof >= size_t.max. (Because Bottom* converts to all other pointer types.) One small issue is that one needs to avoid overflow for the size of a struct that has multiple fields where one of them is of type Bottom.On 7/13/17 2:37 PM, Timon Gehr wrote:I thought bottom.sizeof would be 0.On Thursday, 13 July 2017 at 17:25:18 UTC, Timon Gehr wrote:I wonder if sizeof could be made size_t.max. -- AndreiAnyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype. ...(Actually, there are some complications like the .sizeof property. Anyway, it is clear what the semantics of Bottom are, no matter whether it is subtyping or implicit conversion.)
Jul 16 2017
On Sunday, 16 July 2017 at 12:41:06 UTC, Timon Gehr wrote:It is therefore most natural to say that Bottom.sizeof == ∞.True, but size_t.max doesn't have the properties of ∞. The only sane choice to me seems to be a value of type Bottom, i.e. is(typeof(Bottom.sizeof) == Bottom).
Jul 16 2017
On 16.07.2017 21:49, Guillaume Boucher wrote:On Sunday, 16 July 2017 at 12:41:06 UTC, Timon Gehr wrote:In this case, Bottom.sizeof is a value of type Bottom, which must not exist.It is therefore most natural to say that Bottom.sizeof == ∞.True, but size_t.max doesn't have the properties of ∞. The only sane choice to me seems to be a value of type Bottom, i.e. is(typeof(Bottom.sizeof) == Bottom).
Jul 16 2017
On 07/16/2017 04:15 PM, Timon Gehr wrote:On 16.07.2017 21:49, Guillaume Boucher wrote:Yah that's not workable. Generally the fewer odd exceptions the better, lest all generic code going forward will have to account for those. Timon, since you got your first DIP approved, now is the time to keep the momentum going with a new one! AndreiOn Sunday, 16 July 2017 at 12:41:06 UTC, Timon Gehr wrote:In this case, Bottom.sizeof is a value of type Bottom, which must not exist.It is therefore most natural to say that Bottom.sizeof == ∞.True, but size_t.max doesn't have the properties of ∞. The only sane choice to me seems to be a value of type Bottom, i.e. is(typeof(Bottom.sizeof) == Bottom).
Jul 16 2017
On Sunday, 16 July 2017 at 20:15:11 UTC, Timon Gehr wrote:On 16.07.2017 21:49, Guillaume Boucher wrote:It doesn't exist during runtime, but there's no problem to just generate assert(0) where it's used. I think my interpretation produces the least exceptions, but if you have a better idea go ahead.On Sunday, 16 July 2017 at 12:41:06 UTC, Timon Gehr wrote:In this case, Bottom.sizeof is a value of type Bottom, which must not exist.It is therefore most natural to say that Bottom.sizeof == ∞.True, but size_t.max doesn't have the properties of ∞. The only sane choice to me seems to be a value of type Bottom, i.e. is(typeof(Bottom.sizeof) == Bottom).
Jul 16 2017
On 7/16/2017 1:15 PM, Timon Gehr wrote:In this case, Bottom.sizeof is a value of type Bottom, which must not exist.T.sizeof is a value of type size_t, not type T.
Jul 17 2017
On 7/16/2017 5:41 AM, Timon Gehr wrote:struct S{ T x; Bottom everything; } turns the entire struct into an empty type. It is therefore most natural to say that Bottom.sizeof == ∞. (It's the only choice for which S.sizeof == Bottom.sizeof.) Another way to think about it: If something of type A* converts to something of type B* without problems, then one would expect B.sizeof <= A.sizeof. This would imply that Bottom.sizeof >= size_t.max. (Because Bottom* converts to all other pointer types.) One small issue is that one needs to avoid overflow for the size of a struct that has multiple fields where one of them is of type Bottom.But if Bottom does not exist, then S doesn't exist either, and hence the < size relationship has no meaning. (Reminds me of divide by 0 discussions in calculus class.)
Jul 17 2017
On 18.07.2017 01:01, Walter Bright wrote:... But if Bottom does not exist, then S doesn't exist either, and hence the < size relationship has no meaning. ...Both Bottom and S exist, but they have no instances.(Reminds me of divide by 0 discussions in calculus class.)The reason division by 0 is left undefined is that instead saying 1/0 = ∞ introduces a new number ∞ that does not play nice with the axioms of a complete ordered field. The question for instance size is based on the wrong assumption that there is such an instance. It is true none the less that ∞ is the most natural answer to this question, as if you have multiple answers for instance size, you'd take the supremum. Of course, size_t does not contain ∞.
Jul 17 2017
On 7/17/2017 5:13 PM, Timon Gehr wrote:Infinity makes sense for 1/0, but I don't see how that automatically transfers to size_t.(Reminds me of divide by 0 discussions in calculus class.)The reason division by 0 is left undefined is that instead saying 1/0 = ∞ introduces a new number ∞ that does not play nice with the axioms of a complete ordered field. The question for instance size is based on the wrong assumption that there is such an instance. It is true none the less that ∞ is the most natural answer to this question, as if you have multiple answers for instance size, you'd take the supremum. Of course, size_t does not contain ∞.
Jul 17 2017
On 18.07.2017 03:06, Walter Bright wrote:On 7/17/2017 5:13 PM, Timon Gehr wrote:It does not. The reasoning is unrelated.Infinity makes sense for 1/0, but I don't see how that automatically transfers to size_t.(Reminds me of divide by 0 discussions in calculus class.)The reason division by 0 is left undefined is that instead saying 1/0 = ∞ introduces a new number ∞ that does not play nice with the axioms of a complete ordered field. The question for instance size is based on the wrong assumption that there is such an instance. It is true none the less that ∞ is the most natural answer to this question, as if you have multiple answers for instance size, you'd take the supremum. Of course, size_t does not contain ∞.
Jul 17 2017
On Monday, 17 July 2017 at 23:01:40 UTC, Walter Bright wrote:On 7/16/2017 5:41 AM, Timon Gehr wrote:Strictly speaking it just shouldn't have a sizeof, because sizeof is shorthand for "size of an instance of" (types don't really have sizes, how do I store the type "int" in memory?) and Bottom has no instances. Infinity - or the next best applicable thing size_t.max - is a reasonable standin for an invalid value, except that people will do silly things like `auto paddedSpace = (ReturnType!foo).sizeof + 1;` and then you're in trouble. Better to just not define it. Is there some magic that can be done where all code that makes reference to an instance of Bottom just isn't compiled? I.e. if there happens to be a situation where a function returns Bottom then all code that touches that return type is just ignored?struct S{ T x; Bottom everything; } turns the entire struct into an empty type. It is therefore most natural to say that Bottom.sizeof == ∞. (It's the only choice for which S.sizeof == Bottom.sizeof.) Another way to think about it: If something of type A* converts to something of type B* without problems, then one would expect B.sizeof <= A.sizeof. This would imply that Bottom.sizeof >= size_t.max. (Because Bottom* converts to all other pointer types.) One small issue is that one needs to avoid overflow for the size of a struct that has multiple fields where one of them is of type Bottom.But if Bottom does not exist, then S doesn't exist either, and hence the < size relationship has no meaning. (Reminds me of divide by 0 discussions in calculus class.)
Jul 18 2017
On Tuesday, 18 July 2017 at 10:17:17 UTC, John Colvin wrote:how do I store the type "int" in memory?new Type(TYENUM.Tint32); :o)
Jul 18 2017
On 18.07.2017 12:17, John Colvin wrote:Better to just not define it.That's not an option. Bottom is a subtype of all types. It cannot remove members, even static ones.
Jul 18 2017
On Tuesday, 18 July 2017 at 12:15:06 UTC, Timon Gehr wrote:On 18.07.2017 12:17, John Colvin wrote:Timon, how important is it to actually have bottom ? ... and what does it actually represent ? The closure of all possible types ? like auto but if auto where not replaced ?Better to just not define it.That's not an option. Bottom is a subtype of all types. It cannot remove members, even static ones.
Jul 18 2017
On 18.07.2017 14:19, Stefan Koch wrote:On Tuesday, 18 July 2017 at 12:15:06 UTC, Timon Gehr wrote:D has a C-inspired first-order type system, so it is not necessarily crucial to have it in D. (The reason I got involved in this thread is that it was proposed to add Bottom as a type that is not really a type; 'void' is annoying enough as the 'null' of types. We don't really need another one of those.) Bottom is the most principled way to encode noreturn (but the D type system does not have a tradition of being very principled, so introducing it has a cost that does not really exist the same way in more orthogonal designs: it falls out of them naturally). If you have a very expressive type system, it is important to have empty types, because there you cannot actually decide algorithmically whether any given type is in fact empty. Another reason why one might want an empty type is that it is the neutral element for disjoint union (up to isomorphism). (But D does not have those built-in.)On 18.07.2017 12:17, John Colvin wrote:Timon, how important is it to actually have bottom ?Better to just not define it.That's not an option. Bottom is a subtype of all types. It cannot remove members, even static ones.... and what does it actually represent ?It's a type that has no instances. If I say int foo(); this means foo returns one of {0,1,-1,2,-2,3,-3,...,int.max,int.min}. If I say Bottom foo(); this means foo returns one of {}. I.e., there is no value which foo might return. Hence it cannot return. It can be argued that it is a bit silly to say: int foo() noreturn; I.e., this function returns one of {0,1,-1,2,-2,3,-3,...,int.max,int.min}, but actually, it does not return anything. The first piece of information is redundant.The closure of all possible types ? like auto but if auto where not replaced ?Your intuition is correct. In a higher-order type system, you can have: (∀a. a) foo(); This says that foo returns a value that has any type you wish it to have. Of course, there is no single value that has all types (ignoring e.g. full OO languages that have null references), hence we have no way to actually construct a value satisfying the constraints, so (∀a. a) is an empty type. In languages with subtyping, Bottom is often just a subtype of all other types. (The name "Bottom" stems from here: https://en.wikipedia.org/wiki/Lattice_(order)#Bounded_lattice . The bounded lattice in question is the subtyping lattice, where A ≤ B means A is a subtype of B.) One reason why it is nice to have a bounded subtyping lattice is that then, you can express subtyping constraints uniformly: A≤B does not constraint B if A is Bottom, and it does not constrain A if B is Top.
Jul 18 2017
On Tuesday, 18 July 2017 at 15:26:59 UTC, Timon Gehr wrote:On 18.07.2017 14:19, Stefan Koch wrote:What about void?[...]D has a C-inspired first-order type system, so it is not necessarily crucial to have it in D. (The reason I got involved in this thread is that it was proposed to add Bottom as a type that is not really a type; 'void' is annoying enough as the 'null' of types. We don't really need another one of those.) [...]
Jul 18 2017
On 18.07.2017 20:46, Yuxuan Shui wrote:On Tuesday, 18 July 2017 at 15:26:59 UTC, Timon Gehr wrote:You can't have a value of type void, but it is not empty either. For example, this means that the following transformation is not always valid: return foo(); <-> auto x = foo(); return x;On 18.07.2017 14:19, Stefan Koch wrote:What about void?[...]D has a C-inspired first-order type system, so it is not necessarily crucial to have it in D. (The reason I got involved in this thread is that it was proposed to add Bottom as a type that is not really a type; 'void' is annoying enough as the 'null' of types. We don't really need another one of those.) [...]
Jul 18 2017
On Tuesday, 18 July 2017 at 20:49:56 UTC, Timon Gehr wrote:On 18.07.2017 20:46, Yuxuan Shui wrote:Could you explain why `return foo();` is even legal for a `void foo() {}`? I wasn't aware of it before and the fact that you can (syntactically) return the non-existent return value of `foo` raises cognitive dissonance flags for me. I imagine there's a type system reason?On Tuesday, 18 July 2017 at 15:26:59 UTC, Timon Gehr wrote:You can't have a value of type void, but it is not empty either. For example, this means that the following transformation is not always valid: return foo();On 18.07.2017 14:19, Stefan Koch wrote:What about void?[...]D has a C-inspired first-order type system, so it is not necessarily crucial to have it in D. (The reason I got involved in this thread is that it was proposed to add Bottom as a type that is not really a type; 'void' is annoying enough as the 'null' of types. We don't really need another one of those.) [...]
Jul 18 2017
On Tuesday, 18 July 2017 at 21:35:21 UTC, Moritz Maxeiner wrote:Could you explain why `return foo();` is even legal for a `void foo() {}`?Suppose you are writing a template function that forwards: auto forward(alias fun, T...)(T args) { return fun(args); } It just saves you from having to static if(fun returns void).
Jul 18 2017
On Tuesday, 18 July 2017 at 21:45:27 UTC, Adam D. Ruppe wrote:On Tuesday, 18 July 2017 at 21:35:21 UTC, Moritz Maxeiner wrote:That's a good pragmatic (syntactic sugar) reason, thanks.Could you explain why `return foo();` is even legal for a `void foo() {}`?Suppose you are writing a template function that forwards: auto forward(alias fun, T...)(T args) { return fun(args); } It just saves you from having to static if(fun returns void).
Jul 18 2017
On 18.07.2017 23:35, Moritz Maxeiner wrote:Could you explain why `return foo();` is even legal for a `void foo() {}`?Because the ad-hoc decision to make void a type that is not really a type leads to unnecessary friction, and this exceptional rule removes the friction in one common special case.I wasn't aware of it before and the fact that you can (syntactically) return the non-existent return value of `foo` raises cognitive dissonance flags for me. I imagine there's a type system reason?There should be. foo's return type could be a unit type, with just one value. Then foo does have a return value, but it is always the same and so does not need to be explicitly tracked.
Jul 18 2017
On 07/15/2017 11:30 PM, Walter Bright wrote:On 7/13/2017 5:18 PM, Andrei Alexandrescu wrote:My thinking comes from bottom being the subtype of all types in the universe. Therefore, it must include the state of all types. But let's wait for Timon. -- AndreiOn 7/13/17 2:37 PM, Timon Gehr wrote:I thought bottom.sizeof would be 0.On Thursday, 13 July 2017 at 17:25:18 UTC, Timon Gehr wrote:I wonder if sizeof could be made size_t.max. -- AndreiAnyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype. ...(Actually, there are some complications like the .sizeof property. Anyway, it is clear what the semantics of Bottom are, no matter whether it is subtyping or implicit conversion.)
Jul 16 2017
On 07/13/2017 01:25 PM, Timon Gehr wrote:On Wednesday, 12 July 2017 at 14:23:15 UTC, Andrei Alexandrescu wrote:Sorry missed those... let's see:On 07/12/2017 05:32 AM, Timon Gehr wrote:I might do that, however there are a couple of open questions (see below).On 09.07.2017 23:45, Meta wrote:Timon, I think you're very well positioned to author a DIP on this. Getting through acceptance on your static foreach DIP seems to not require a lot of extra work. ...... Another case that we should probably just statically disallow: ... > This obviously doesn't make any sense anyway ... > I don't see a reason for us to ever need to do thatSorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.It's perfectly fine to have a type of types which is its own type, especially if you allow non-termination.<nod>I'm saying the D notion of subtyping is a bit messy because memory layout matters and subtyping and implicit conversion are not the same thing. Anyway, my assertion that Bottom cannot be a subtype of all other types was actually incorrect: the compiler does not need to generate code for implicit conversion from Bottom to some other type, so it can be treated as a subtype.<nod>typeof(assert(0))* and typeof(assert(0))[] will hence be subtypes of all other pointer and array types respectively.Wow. Cool!An issue is that we already have typeof(null). typeof(null) and typeof(assert(0))* are two ways to specify almost the same thing. One question is whether typeof(assert(0))* and typeof(null) should be the same, or if the former should not implicitly convert to class references. I have also argued in the past that there should be a separate typeof([]). This role would now be filled by typeof(assert(0))[]. However, changing the type of '[]' may break code.You're on to something here. Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created. The latter is a mere consequence of the former.I think the DIP should introduce conversion from the start, as conversion is easy to support. It is simple to support in the front end and the code gen for it is literally to emit nothing.Fantastic. Please kick it off. Thanks!! Andrei
Jul 16 2017
On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:I'd really prefer if you avoided the whole `typeof(assert(0))` thing. First off, it's way too verbose for a simple concept. In general, code is much more readable when you can read functions as `Type functionName(args)`, rather than template-style `expr(valueof!(thing + otherThing).typeof) functionName(args)`, so I think it would be better not to encourage adding more expressions as return types. I think the following: noreturn_t logThenQuit(string message) is much more readable and obvious (especially to a beginner) than: typeof(*null) logThenQuit(string message) Of course, you could implement typeof(*null); and also add noreturn_t as an alias; it might be a good compromise; I'd still dislike it because it encourages people to use the verbose hard to understand version. The second reason I don't like it is that I feel it's just trying to be clever for the sake of cleverness. I don't think we need a language feature that perfectly matches the idea of not returning from a function on a deep, philosophical level; we just need a way to tell the type system "Hey, this function doesn't return!". I don't think `typeof(*null)`, or `typeof(assert(0))` brings any advantage in term of real life user code, and I don't think it's worth the confused users that would look at code and go "Uh? What is the type of *null?" or "I thought assert was void! What would you get the type of assert()?".An issue is that we already have typeof(null). typeof(null) and typeof(assert(0))* are two ways to specify almost the same thing. One question is whether typeof(assert(0))* and typeof(null) should be the same, or if the former should not implicitly convert to class references. I have also argued in the past that there should be a separate typeof([]). This role would now be filled by typeof(assert(0))[]. However, changing the type of '[]' may break code.You're on to something here. Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created. The latter is a mere consequence of the former.
Jul 17 2017
On Monday, 17 July 2017 at 15:39:30 UTC, Olivier FAURE wrote:On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:Yes, this!I'd really prefer if you avoided the whole `typeof(assert(0))` thing. First off, it's way too verbose for a simple concept. In general, code is much more readable when you can read functions as `Type functionName(args)`, rather than template-style `expr(valueof!(thing + otherThing).typeof) functionName(args)`, so I think it would be better not to encourage adding more expressions as return types. I think the following: noreturn_t logThenQuit(string message) is much more readable and obvious (especially to a beginner) than: typeof(*null) logThenQuit(string message) Of course, you could implement typeof(*null); and also add noreturn_t as an alias; it might be a good compromise; I'd still dislike it because it encourages people to use the verbose hard to understand version. The second reason I don't like it is that I feel it's just trying to be clever for the sake of cleverness. I don't think we need a language feature that perfectly matches the idea of not returning from a function on a deep, philosophical level; we just need a way to tell the type system "Hey, this function doesn't return!". I don't think `typeof(*null)`, or `typeof(assert(0))` brings any advantage in term of real life user code, and I don't think it's worth the confused users that would look at code and go "Uh? What is the type of *null?" or "I thought assert was void! What would you get the type of assert()?".An issue is that we already have typeof(null). typeof(null) and typeof(assert(0))* are two ways to specify almost the same thing. One question is whether typeof(assert(0))* and typeof(null) should be the same, or if the former should not implicitly convert to class references. I have also argued in the past that there should be a separate typeof([]). This role would now be filled by typeof(assert(0))[]. However, changing the type of '[]' may break code.You're on to something here. Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created. The latter is a mere consequence of the former.
Jul 17 2017
On 7/17/17 11:39 AM, Olivier FAURE wrote:I'd really prefer if you avoided the whole `typeof(assert(0))` thing. First off, it's way too verbose for a simple concept.Noted, thanks. I won't debate this much but for now I disagree. The "no return" type has several particular properties that set it aside (e.g. it's impossible to implement as a library, does things no other types do, etc). It's also used rarely. Therefore it stands to reason to consider an attention-seeking notation for it. The upside of this is we can always add an alias to give the type a name if we so wish. For now I'd want to experiment with using typeof as notation. Andrei
Jul 17 2017
On Mon, Jul 17, 2017 at 02:10:27PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:On 7/17/17 11:39 AM, Olivier FAURE wrote:[...] IMO, the observations "used rarely" and "attention-seeking notation" are better satisfied by an attribute named noreturn than some strange, convoluted, arcane invocation like `typeof(assert(0))`. Because: (1) "used rarely" means it should be as non-intrusive as possible as far as the language is concerned -- we should not spend a lot of language real estate on something that's rarely used, nor should it be something complicated to implement, and/or introduces tricky corner cases that we're likely to get wrong on first attempt. A noreturn attribute is non-intrusive -- doesn't interact with anything else in the language, easy to implement -- has no tricky corner cases. (2) "attention-seeking": an arcane invocation like `typeof(assert(0))` is harder to parse and therefore more likely to invite people to just gloss over it as "incomprehensible gibberish that I'll just ignore unless I have to look further", than catch people's attention. Whereas an annotation like ` noreturn` is immediately obvious by its very presence, with a name that instantly tells you what it does while it still holds your attention. I see `typeof(assert(0))` as the same kind of over-engineering of something trivially solved and relatively unimportant ("rarely used") that has unfortunately plagued C++ design and led C++ to become the mess it is today. It's sad to see D start down the same path... T -- I see that you JS got Bach.I'd really prefer if you avoided the whole `typeof(assert(0))` thing. First off, it's way too verbose for a simple concept.Noted, thanks. I won't debate this much but for now I disagree. The "no return" type has several particular properties that set it aside (e.g. it's impossible to implement as a library, does things no other types do, etc). It's also used rarely. Therefore it stands to reason to consider an attention-seeking notation for it.
Jul 17 2017
On Monday, 17 July 2017 at 18:54:37 UTC, H. S. Teoh wrote:On Mon, Jul 17, 2017 at 02:10:27PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:+1000! You've said it all!On 7/17/17 11:39 AM, Olivier FAURE wrote:[...] IMO, the observations "used rarely" and "attention-seeking notation" are better satisfied by an attribute named noreturn than some strange, convoluted, arcane invocation like `typeof(assert(0))`. Because: (1) "used rarely" means it should be as non-intrusive as possible as far as the language is concerned -- we should not spend a lot of language real estate on something that's rarely used, nor should it be something complicated to implement, and/or introduces tricky corner cases that we're likely to get wrong on first attempt. A noreturn attribute is non-intrusive -- doesn't interact with anything else in the language, easy to implement -- has no tricky corner cases. (2) "attention-seeking": an arcane invocation like `typeof(assert(0))` is harder to parse and therefore more likely to invite people to just gloss over it as "incomprehensible gibberish that I'll just ignore unless I have to look further", than catch people's attention. Whereas an annotation like ` noreturn` is immediately obvious by its very presence, with a name that instantly tells you what it does while it still holds your attention. I see `typeof(assert(0))` as the same kind of over-engineering of something trivially solved and relatively unimportant ("rarely used") that has unfortunately plagued C++ design and led C++ to become the mess it is today. It's sad to see D start down the same path... TI'd really prefer if you avoided the whole `typeof(assert(0))` thing. First off, it's way too verbose for a simple concept.Noted, thanks. I won't debate this much but for now I disagree. The "no return" type has several particular properties that set it aside (e.g. it's impossible to implement as a library, does things no other types do, etc). It's also used rarely. Therefore it stands to reason to consider an attention-seeking notation for it.
Jul 17 2017
On 7/17/17 2:54 PM, H. S. Teoh via Digitalmars-d wrote:I see `typeof(assert(0))` as the same kind of over-engineering of something trivially solved and relatively unimportant ("rarely used") that has unfortunately plagued C++ design and led C++ to become the mess it is today. It's sad to see D start down the same path...There is no reason to over-react seeing as the option of adding a name is easy and always available; removing it not so much. This makes taking options in language design highly asymmetrical. Having the type available as an actual expression brings good options on the table because people can return those on unreachable paths etc. -- Andrei
Jul 17 2017
On Mon, Jul 17, 2017 at 06:42:02PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:On 7/17/17 2:54 PM, H. S. Teoh via Digitalmars-d wrote:Actually, I don't really care enough about this issue to want it to be implemented one way or another, as long as there is *some* way to annotate a non-returning function. But the point is that so much time and effort is being spent on discussing and designing a feature that you have admitted yourself to be "rarely used". As a disinterested bystander I find it somewhat amusing (and sad) to see so much over-engineering of an overly-complex system involving a new basic type in the language, which in turn entails all sorts of corner cases in how it will interact with existing types and constructs, not to mention the implementation complexities that will be involved to pull it off -- all for what? Just to be able to say "function F doesn't return". Seems like disproportionate effort for only marginal returns (har har). T -- Those who don't understand D are condemned to reinvent it, poorly. -- Daniel NI see `typeof(assert(0))` as the same kind of over-engineering of something trivially solved and relatively unimportant ("rarely used") that has unfortunately plagued C++ design and led C++ to become the mess it is today. It's sad to see D start down the same path...There is no reason to over-react seeing as the option of adding a name is easy and always available; removing it not so much. This makes taking options in language design highly asymmetrical. Having the type available as an actual expression brings good options on the table because people can return those on unreachable paths etc. -- Andrei
Jul 17 2017
On Monday, 17 July 2017 at 23:26:18 UTC, H. S. Teoh wrote:Actually, I don't really care enough about this issue to want it to be implemented one way or another, as long as there is *some* way to annotate a non-returning function. But the point is that so much time and effort is being spent on discussing and designing a feature that you have admitted yourself to be "rarely used". As a disinterested bystander I find it somewhat amusing (and sad) to see so much over-engineering of an overly-complex system involving a new basic type in the language, which in turn entails all sorts of corner cases in how it will interact with existing types and constructs, not to mention the implementation complexities that will be involved to pull it off -- all for what? Just to be able to say "function F doesn't return". Seems like disproportionate effort for only marginal returns (har har).I can't agree more. This is textbook procrastination and bike-shedding [1]! There are dozens of open regressions that could have fixed or great, stalled PRs that could have been reviewed. In fact if only PRs would be as heartily reviewed as the discussion here, things like the fact that DMD leaks all symbols when imported selectively [2] would have been uncovered earlier. [1] https://en.wikipedia.org/wiki/Law_of_triviality [2] https://github.com/dlang/phobos/pull/5584#issuecomment-314910297
Jul 17 2017
On 7/17/2017 5:06 PM, Seb via Digitalmars-d wrote:I can't agree more. This is textbook procrastination and bike-shedding [1]! There are dozens of open regressions that could have fixed or great, stalled PRs that could have been reviewed. In fact if only PRs would be as heartily reviewed as the discussion here, things like the fact that DMD leaks all symbols when imported selectively [2] would have been uncovered earlier. [1] https://en.wikipedia.org/wiki/Law_of_triviality [2] https://github.com/dlang/phobos/pull/5584#issuecomment-314910297Semi-valid, but <insert rational and well written paragraph on the false fungability of time and effort>.
Jul 17 2017
On 7/17/2017 4:26 PM, H. S. Teoh via Digitalmars-d wrote:But the point is that so much time and effort is being spent on discussing and designing a feature that you have admitted yourself to be "rarely used". As a disinterested bystander I find it somewhat amusing (and sad) to see so much over-engineering of an overly-complex system involving a new basic type in the language, which in turn entails all sorts of corner cases in how it will interact with existing types and constructs, not to mention the implementation complexities that will be involved to pull it off -- all for what? Just to be able to say "function F doesn't return". Seems like disproportionate effort for only marginal returns (har har).The issue here (for me) is to have a type system that matches type systems used in type theory. D already has strong abilities to do type calculus. Instead of inventing our own whackadoodle scheme, one hack at a time, why not match existing type calculus? Then, attempts to do type calculus in D will work as worked out by type theory. Or, we could go with the C++ approach which historically is to add an ad-hoc solution for an existing problem, and then another ad-hoc solution for the whacky effects that turned out to have, rinse, repeat. (Look at all the different ways to do type deduction, a horrifying consequence. Or function overloading, which started with complex special cases, then changed to partial ordering for special cases.) There is just something fundamentally wrong with: noreturn int foo(); returning a value yet not returning. It smacks of "the language designer(s) are idiots." It winds up complicating things like: auto x = a ? b : foo(); What is the type of x? noreturn means a special case. A proper bottom type means it is not a special case. I recall that Rust initially did noreturn as a special case, and later replaced that with a bottom type and integrated it into the type system. I understand that this had a positive ripple effect, such as reducing special cases in user generic code. Noreturn functions are just a happy fallout of doing this correctly in the first place. The language semantics and compiler internals should be simpler and cleaner by using accepted type theory.
Jul 18 2017
On Tuesday, 18 July 2017 at 22:03:27 UTC, Walter Bright wrote:On 7/17/2017 4:26 PM, H. S. Teoh via Digitalmars-d wrote:But the point is that so much time and effort is being spent on discussing and designing a feature that you have admitted yourself to be "rarely used". As a disinterested bystander I find it somewhat amusing (and sad) to see so much over-engineering of an overly-complex system involving a new basic type in the language, which in turn entails all sorts of corner cases in how it will interact with existing types and constructs, not to mention the implementation complexities that will be involved to pull it off -- all for what? Just to be able to say "function F doesn't return". Seems like disproportionate effort for only marginal returns (har har).The issue here (for me) is to have a type system that matches type systems used in type theory. D already has strong abilities to do type calculus. Instead of inventing our own whackadoodle scheme, one hack at a time, why not match existing type calculus? Then, attempts to do type calculus in D will work as worked out by type theory. Or, we could go with the C++ approach which historically is to add an ad-hoc solution for an existing problem, and then another ad-hoc solution for the whacky effects that turned out to have, rinse, repeat. (Look at all the different ways to do type deduction, a horrifying consequence. Or function overloading, which started with complex special cases, then changed to partial ordering for special cases.)[...]Agreed. Discovered vs invented as Philip Wadler classifies the two approaches in his talk: https://www.youtube.com/watch?v=IOiZatlZtGU, which I highly recommend watching.
Jul 18 2017
On Tuesday, 18 July 2017 at 22:03:27 UTC, Walter Bright wrote:The issue here (for me) is to have a type system that matches type systems used in type theory. D already has strong abilities to do type calculus. Instead of inventing our own whackadoodle scheme, one hack at a time, why not match existing type calculus? Then, attempts to do type calculus in D will work as worked out by type theory. Or, we could go with the C++ approach which historically is to add an ad-hoc solution for an existing problem, and then another ad-hoc solution for the whacky effects that turned out to have, rinse, repeat. (Look at all the different ways to do type deduction, a horrifying consequence. Or function overloading, which started with complex special cases, then changed to partial ordering for special cases.) There is just something fundamentally wrong with: noreturn int foo();I would understand it to mean that if it were to return, foo would return an int but it is undefined behaviour for foo to dynamically return.returning a value yet not returning. It smacks of "the language designer(s) are idiots." It winds up complicating things like: auto x = a ? b : foo(); What is the type of x? noreturn means a special case. A proper bottom type means it is not a special case.int. noreturn need not pollute the type, given the use cases for noreturn. Namely to document that the function does not dynamically return and aid the compiler in optimisation (are there any other uses?). `assert(0);` is already accepted in the front end as an acceptable return "value" for any type e.g. in Bar foo(int x) { foreach (e; data[]) if (e.x == x) return e; assert(0); }The language semantics and compiler internals should be simpler and cleaner by using accepted type theory.Not for LDC or GDC. They already have the ability to signal to their backends that a function does not dynamically return. as I have posted before, one can do (in core.attribute), enum __noreturn; version(LDC) { import ldc.attributes : llvmAttr; alias noreturn = AliasSeq!(llvmAttr("noreturn"),__noreturn); } else version(GNU) { import gcc.attribute : llvmAttr; alias noreturn = AliasSeq!(attribute("noreturn"),__noreturn); } else // DMD { alias noreturn = __noreturn; } for a complete implementation for LDC and GDC, and DMD can do whatever it needs to with the presence of __noreturn, including fronted semantic analysis.
Jul 18 2017
On 7/18/2017 5:54 PM, Nicholas Wilson wrote:That's the C++ behavior. I know we are all accustomed to it and hence think it is intuitive, but it isn't. I know I've had a hard time breaking free of this sort of thinking, having been so deeply immersed in C++ for so long.There is just something fundamentally wrong with: noreturn int foo();I would understand it to mean that if it were to return, foo would return an int but it is undefined behaviour for foo to dynamically return.And if b is of type `T`? It doesn't make sense to have to give a type to something that does not return. ( noreturn functions are usually typed as returning `void` anyway, but that still doesn't make much sense.) I know how noreturn attributes work - I implemented them decades ago in DMC and DMC++. They are supported by the DMD optimizer and back end. But they are a hack to the type system, and I suggest an unnecessary one. The backends for DMD, LDC and GDC would not be affected at all by the addition of a bottom type to the front end, and it would be trivial for the glue code to add the noreturn attribute for functions that return the bottom type.returning a value yet not returning. It smacks of "the language designer(s) are idiots." It winds up complicating things like: auto x = a ? b : foo(); What is the type of x? noreturn means a special case. A proper bottom type means it is not a special case.int.
Jul 18 2017
On Wednesday, 19 July 2017 at 01:52:30 UTC, Walter Bright wrote:I know how noreturn attributes work - I implemented them decades ago in DMC and DMC++. They are supported by the DMD optimizer and back end. But they are a hack to the type system, and I suggest an unnecessary one.It describe the behaviour of the function: I think it is neither necessary nor a good idea to express it as a type. assert(0) is already accepted as a valid return statement of any type.The backends for DMD, LDC and GDC would not be affected at all by the addition of a bottom type to the front end, and it would be trivial for the glue code to add the noreturn attribute for functions that return the bottom type.Said glue would be unnecessary with an attribute *that already exists*.
Jul 18 2017
On 7/18/2017 7:14 PM, Nicholas Wilson wrote:It describe the behaviour of the function: I think it is neither necessary nor a good idea to express it as a type. assert(0) is already accepted as a valid return statement of any type.I can't continue this without being repetitive, so we'll just have to disagree.In the glue code, replace: if (function attribute is 'noreturn') set backend attribute to 'noreturn'; with: if (function return type is 'bottom') set backend attribute to 'noreturn'; set backend function return type to 'void'; and it should be ready to rock :-)The backends for DMD, LDC and GDC would not be affected at all by the addition of a bottom type to the front end, and it would be trivial for the glue code to add the noreturn attribute for functions that return the bottom type.Said glue would be unnecessary with an attribute *that already exists*.
Jul 18 2017
On Monday, 17 July 2017 at 18:54:37 UTC, H. S. Teoh wrote:IMO, the observations "used rarely" and "attention-seeking notation" are better satisfied by an attribute named noreturn than some strange, convoluted, arcane invocation like `typeof(assert(0))`. Because: (1) "used rarely" means it should be as non-intrusive as possible as far as the language is concerned -- we should not spend a lot of language real estate on something that's rarely used, nor should it be something complicated to implement, and/or introduces tricky corner cases that we're likely to get wrong on first attempt. A noreturn attribute is non-intrusive -- doesn't interact with anything else in the language, easy to implement -- has no tricky corner cases. (2) "attention-seeking": an arcane invocation like `typeof(assert(0))` is harder to parse and therefore more likely to invite people to just gloss over it as "incomprehensible gibberish that I'll just ignore unless I have to look further", than catch people's attention. Whereas an annotation like ` noreturn` is immediately obvious by its very presence, with a name that instantly tells you what it does while it still holds your attention. I see `typeof(assert(0))` as the same kind of over-engineering of something trivially solved and relatively unimportant ("rarely used") that has unfortunately plagued C++ design and led C++ to become the mess it is today. It's sad to see D start down the same path... T(3) LDC and GDC already have an implementation in the form of an attribute so we can just have an AliasSeq of the front end recognised attribute and the one for GDC and LDC (assuming my attributes DIP goes through).
Jul 17 2017
On Monday, 17 July 2017 at 18:54:37 UTC, H. S. Teoh wrote:On Mon, Jul 17, 2017 at 02:10:27PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:object.d: alias noreturn = typeof(assert(0)); Atila[...][...] IMO, the observations "used rarely" and "attention-seeking notation" are better satisfied by an attribute named noreturn than some strange, convoluted, arcane invocation like `typeof(assert(0))`. Because: [...]
Jul 18 2017
On Monday, 17 July 2017 at 18:10:27 UTC, Andrei Alexandrescu wrote:On 7/17/17 11:39 AM, Olivier FAURE wrote:Fair enough.I'd really prefer if you avoided the whole `typeof(assert(0))` thing. First off, it's way too verbose for a simple concept.Noted, thanks. I won't debate this much but for now I disagree.
Jul 18 2017
On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created.That sounds more like a top type, though, because as you said it can be "anything". A bottom type can not be anything, but only nothing.
Jul 19 2017
On Wednesday, 19 July 2017 at 10:24:35 UTC, Marc Schütz wrote:On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:It's the bottom. Bottom is to Types, as Object is to Classes.Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created.That sounds more like a top type, though, because as you said it can be "anything". A bottom type can not be anything, but only nothing.
Jul 19 2017
On 19.07.2017 12:35, Stefan Koch wrote:On Wednesday, 19 July 2017 at 10:24:35 UTC, Marc Schütz wrote:No. Bottom is to types as typeof(null) is to class types.On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:It's the bottom. Bottom is to Types, as Object is to Classes.Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created.That sounds more like a top type, though, because as you said it can be "anything". A bottom type can not be anything, but only nothing.
Jul 19 2017
On Wednesday, 19 July 2017 at 10:44:22 UTC, Timon Gehr wrote:No. Bottom is to types as typeof(null) is to class types.I fear you lost me again :) as far as I understood your previous explanation, every type is a subtype of bottom, is that incorrect ?
Jul 19 2017
On 19.07.2017 12:59, Stefan Koch wrote:On Wednesday, 19 July 2017 at 10:44:22 UTC, Timon Gehr wrote:It is the other way around. Bottom is a subtype of every type / a value of type bottom can be used to construct a value for any other type.No. Bottom is to types as typeof(null) is to class types.I fear you lost me again :) as far as I understood your previous explanation, every type is a subtype of bottom, is that incorrect ?
Jul 19 2017
On Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:a value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).
Jul 19 2017
Am Wed, 19 Jul 2017 12:13:40 +0000 schrieb Moritz Maxeiner <moritz ucworks.org>:On Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:2018, Dlang is now an esoteric language. After a long bike-shedding the "bottom type" has been named "nirvana" and assigning it to a variable of any other type signifies intent to give the program a reincarnation. On Posix this was efficiently implemented via fork and exec, Windows implementation is still suffering from bad vibes (bugs). Phobos comes in several flavors now, because it was discovered that one Phobos can never be enough to capture all the worlds paradigms and was considered the main offender to peace on the forums. So there is now an assembly optimized fast Phobos for performance fans without safety nor GC; a type theory Phobos that tries hard to hide the fact that structs have a fixed data layout and makes types first class citizens, but doesn't interop with C at all; an auto-decoding Phobos; and a batteries included Phobos with database drivers, audio, image and GUI bindings. -- Marcoa value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).
Jul 19 2017
On 19.07.2017 14:13, Moritz Maxeiner wrote:On Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:https://en.wikipedia.org/wiki/Principle_of_explosiona value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).
Jul 19 2017
On Wednesday, 19 July 2017 at 14:32:24 UTC, Timon Gehr wrote:On 19.07.2017 14:13, Moritz Maxeiner wrote:I am aware, but once a statement (and its negation) can be inferred from the same (false) proposition, one isn't reasoning anymore - and more importantly its not useful w.r.t. explaining what the bottom type is.On Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:https://en.wikipedia.org/wiki/Principle_of_explosiona value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).
Jul 19 2017
On 19.07.2017 16:47, Moritz Maxeiner wrote:On Wednesday, 19 July 2017 at 14:32:24 UTC, Timon Gehr wrote:I disagree with both of those statements, but I'm not sure how any of this relates to the true sentence I wrote that you seemed to criticize.On 19.07.2017 14:13, Moritz Maxeiner wrote:I am aware, but once a statement (and its negation) can be inferred from the same (false) proposition, one isn't reasoning anymore - and more importantly its not useful w.r.t. explaining what the bottom type is.On Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:https://en.wikipedia.org/wiki/Principle_of_explosiona value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).
Jul 19 2017
On Wednesday, 19 July 2017 at 14:52:28 UTC, Timon Gehr wrote:On 19.07.2017 16:47, Moritz Maxeiner wrote:The sentence I quoted states you can use a value of type bottom to construct a value of any other type; this means the existence of such a value of type bottom becomes an implicit premise. As the bottom type is defined as having no values that premise does not hold, i.e. you can infer both "a value of type bottom can be used to construct a value for any other type." and "a value of type bottom cannot be used to construct a value for any other type." from it (principle of explosion, as you quoted). My original criticism was meant to convey that I do not consider the quoted sentence as being helpful w.r.t. explaining what the bottom type is (which the rest of the post I quoted the sentence from did quite well).On Wednesday, 19 July 2017 at 14:32:24 UTC, Timon Gehr wrote:I disagree with both of those statements, but I'm not sure how any of this relates to the true sentence I wrote that you seemed to criticize.On 19.07.2017 14:13, Moritz Maxeiner wrote:I am aware, but once a statement (and its negation) can be inferred from the same (false) proposition, one isn't reasoning anymore - and more importantly its not useful w.r.t. explaining what the bottom type is.On Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:https://en.wikipedia.org/wiki/Principle_of_explosiona value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).
Jul 19 2017
On 19.07.2017 17:14, Moritz Maxeiner wrote:On Wednesday, 19 July 2017 at 14:52:28 UTC, Timon Gehr wrote:Not really; see below.On 19.07.2017 16:47, Moritz Maxeiner wrote:The sentence I quoted states you can use a value of type bottom to construct a value of any other type; this means the existence of such a value of type bottom becomes an implicit premise.On Wednesday, 19 July 2017 at 14:32:24 UTC, Timon Gehr wrote:I disagree with both of those statements, but I'm not sure how any of this relates to the true sentence I wrote that you seemed to criticize.On 19.07.2017 14:13, Moritz Maxeiner wrote:I am aware, but once a statement (and its negation) can be inferred from the same (false) proposition, one isn't reasoning anymore - and more importantly its not useful w.r.t. explaining what the bottom type is.On Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:https://en.wikipedia.org/wiki/Principle_of_explosiona value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).As the bottom type is defined as having no values that premise does not hold, i.e. you can infer both "a value of type bottom can be used to construct a value for any other type." and "a value of type bottom cannot be used to construct a value for any other type." from it (principle of explosion, as you quoted). My original criticism was meant to convey that I do not consider the quoted sentence as being helpful w.r.t. explaining what the bottom type is (which the rest of the post I quoted the sentence from did quite well).What I said does not /use/ the principle of explosion; it states it. What I am saying is: in a language with a bottom type, we can create a function: T f(T)(Bottom b){ return b; // assuming b converts to all types implicitly. } Within the function body, b is a value of type Bottom. We use a value of type Bottom to create a value of any type we want. The reason why I included that part of the sentence was: Not all programming languages have subtyping, but all programming languages with a bottom type allow a function of the above type to be constructed. (It's the induction principle for empty algebraic data types.) For any T, the type of &f!T is T delegate(Bottom), or in different notation: f: ∀a. ⊥ → a. I.e., the type of f is the principle of explosion.
Jul 19 2017
Timon Gehr <timon.gehr gmx.ch> wrote:[...] What I am saying is: in a language with a bottom type, we can create a function: T f(T)(Bottom b){ return b; // assuming b converts to all types implicitly. } Within the function body, b is a value of type Bottom. We use a value of type Bottom to create a value of any type we want.As I understand it, you can create *variables* of type Bottom but not *values*. b is a variable, not a value, you cannot actually call that function at runtime.
Jul 19 2017
On 20.07.2017 00:21, Tobias Müller wrote:Timon Gehr <timon.gehr gmx.ch> wrote:Correct.[...] What I am saying is: in a language with a bottom type, we can create a function: T f(T)(Bottom b){ return b; // assuming b converts to all types implicitly. } Within the function body, b is a value of type Bottom. We use a value of type Bottom to create a value of any type we want.As I understand it, you can create *variables* of type Bottom but not *values*.b is a variable, not a value,Within the function body, it is.you cannot actually call that function at runtime.Correct.
Jul 20 2017
On 07/19/2017 10:32 AM, Timon Gehr wrote:On 19.07.2017 14:13, Moritz Maxeiner wrote:Didn't know "false implies anything" comes with such a nice name. Thanks! -- AndreiOn Wednesday, 19 July 2017 at 11:35:47 UTC, Timon Gehr wrote:https://en.wikipedia.org/wiki/Principle_of_explosiona value of type bottom can be used to construct a value for any other type.AFAIK from type theory, bottom is defined as having no values (so one can't reason about the relationship of such non-existent value(s) to values of other types).
Jul 21 2017
I didn't look through all of the replies to this thread to check that this hasn't been mentioned yet, but TypeScript uses the 'never' return type for functions that never return. https://www.typescriptlang.org/docs/handbook/basic-types.html#never The type isn't used for any optimisations, it's only used for preventing you from adding lines of code after functions which never return. (Say if they run "forever" or throw exceptions.) I thought it would be worth mentioning how another language handles this currently.
Jul 24 2017
On Wednesday, 19 July 2017 at 10:35:37 UTC, Stefan Koch wrote:On Wednesday, 19 July 2017 at 10:24:35 UTC, Marc Schütz wrote:Actually, Object should be considered the Top type. All Classes are sub-classes of Object.On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:It's the bottom. Bottom is to Types, as Object is to Classes.Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created.That sounds more like a top type, though, because as you said it can be "anything". A bottom type can not be anything, but only nothing.
Jul 20 2017
On 19.07.2017 12:24, Marc Schütz wrote:On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:There is nothing that can be anything, but anything can be top. Natural language has an unfortunate tendency to blur the distinction between dual concepts as it gets more informal.Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it: null: a pointer to anything. But can't be dereferenced. *null: well, therefore... anything. But can't be created.That sounds more like a top type, though, because as you said it can be "anything". A bottom type can not be anything, but only nothing.
Jul 19 2017
On 7/9/2017 12:30 PM, Meta wrote:[...]Some great info and links. It's a compelling argument to add a bottom type.
Jul 09 2017
On 07/09/2017 03:30 PM, Meta wrote:alias Bottom = typeof(assert(0)); //for convenienceSpeaking of which, I think we shouldn't give it a name, same as typeof(null). Just keep it typeof(assert(0)). Then wherever it occurs it is immediately clear it has a special status requiring a second look (which it does). -- Andrei
Jul 09 2017
On 07/09/2017 06:32 PM, Andrei Alexandrescu wrote:On 07/09/2017 03:30 PM, Meta wrote:And btw this is technically a breaking change because somebody somewhere is liable to write code like: void fun() { ... return assert(0); } I'm not too worried about it though, and in fact we may even still accept it because hey typeof(assert(0)) converts to anything so how about it even converts to void. Andreialias Bottom = typeof(assert(0)); //for convenienceSpeaking of which, I think we shouldn't give it a name, same as typeof(null). Just keep it typeof(assert(0)). Then wherever it occurs it is immediately clear it has a special status requiring a second look (which it does). -- Andrei
Jul 09 2017
On Sunday, 9 July 2017 at 18:01:08 UTC, Andrei Alexandrescu wrote:On 07/09/2017 12:19 PM, Steven Schveighoffer wrote:I would argue a type hack in the compiler doesn't compare to the principaled use of existing syntax and semantics ;) -SteveIt's no more of a hack than leaving assert(0) in release code.I wouldn't argue that. I do argue it's a hack compared to the principled solution of a bottom type.
Jul 09 2017
On Sun, Jul 09, 2017 at 02:01:08PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:On 07/09/2017 12:19 PM, Steven Schveighoffer wrote:I like out{ assert(0); } for pretty much the same reasons as Steven lists. The biggest pro is that **the language already supports it*. Contract syntax already is meant to signal intent, and assert(0) signals "never gets here". You can't find a better solution than this. All the other alternatives require adding even more baggage to an already heavy language, or compiler voodoo to recognize a particular pattern of defining a type. I'd say out{assert(0);} is the principled solution -- expressing something the current language can already express, and it's the other alternatives that are "exotic". T -- Freedom: (n.) Man's self-given right to be enslaved by his own depravity.It's no more of a hack than leaving assert(0) in release code.I wouldn't argue that. I do argue it's a hack compared to the principled solution of a bottom type. -- Andrei
Jul 09 2017
On 07/09/2017 05:14 PM, H. S. Teoh via Digitalmars-d wrote:On Sun, Jul 09, 2017 at 02:01:08PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:Prioritizing "path of least resistense" over "this is cleaner and more principled" is the hallmark of C++-style langauge design. Please, please, please, let's stay away from that path.On 07/09/2017 12:19 PM, Steven Schveighoffer wrote:I like out{ assert(0); } for pretty much the same reasons as Steven lists. The biggest pro is that **the language already supports it*. Contract syntax already is meant to signal intent, and assert(0) signals "never gets here". You can't find a better solution than this. All the other alternatives require adding even more baggage to an already heavy language, or compiler voodoo to recognize a particular pattern of defining a type. I'd say out{assert(0);} is the principled solution -- expressing something the current language can already express, and it's the other alternatives that are "exotic".It's no more of a hack than leaving assert(0) in release code.I wouldn't argue that. I do argue it's a hack compared to the principled solution of a bottom type. -- Andrei
Jul 09 2017
On Monday, 10 July 2017 at 03:25:26 UTC, Nick Sabalausky (Abscissa) wrote:On 07/09/2017 05:14 PM, H. S. Teoh via Digitalmars-d wrote:While I agree with your sentiment in principle (heh), we need to keep in mind it purpose. So far I count four requirements of a solution: documentation optimisation ability to statically reflect upon ability to implement Of the approached listed only out{assert(0);} fails the static reflection check. Which leaves 1) noreturn 2) disable(return) 3)none 1 & 2 are almost functionally identical in their implementation with 2 requiring some semantic hacks to the compiler and precludes the AliasSeq approach below, so I consider 1 to be superior to 2. this leaves 1 & 3. w.r.t documentation for 1 this is a solved problem, it becomes part of the type signature and will be picked up in the documentation building tools. 3 it becomes part of the return type and can also be solved by documenting the type. w.r.t optimisation assuming both 1 & 3 impact DMD equally then there is no difference except that: Implementation: 3 would require GDC and LDC to make changes _when they already have a solution_. It would also be _considerably more work_ than 1, and would be _backwards incompatible_ with older compilers. In fact 1 could be implemented _Immediately_ for GDC and LDC by having enum __noreturn; version (DigitalMars) alias noreturn = __noreturn; else version(GNU) { import gcc.attributes : attribute; alias noreturn = AliasSeq!(__noreturn,attribute("noreturn")); } else version (LDC) { import ldc.attributes : llvmAttr; alias noreturn = AliasSeq!(__noreturn,llvmAttr("noreturn")); } and reflect upon the presence of __noreturn; I am strongly against the need to complicate the compiler when an equal (IMHO better) solution _already exists_, for extremely marginal gain: anyone seriously concerned about the small optimisation benefit will already be using GDC or LDC, the applicability of noreturn is minuscule, AFAICT C's _exit and an unconditional throw (Exception or Error) are the only functions for which it applies.On Sun, Jul 09, 2017 at 02:01:08PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:Prioritizing "path of least resistense" over "this is cleaner and more principled" is the hallmark of C++-style langauge design. Please, please, please, let's stay away from that path.On 07/09/2017 12:19 PM, Steven Schveighoffer wrote:I like out{ assert(0); } for pretty much the same reasons as Steven lists. The biggest pro is that **the language already supports it*. Contract syntax already is meant to signal intent, and assert(0) signals "never gets here". You can't find a better solution than this. All the other alternatives require adding even more baggage to an already heavy language, or compiler voodoo to recognize a particular pattern of defining a type. I'd say out{assert(0);} is the principled solution -- expressing something the current language can already express, and it's the other alternatives that are "exotic".It's no more of a hack than leaving assert(0) in release code.I wouldn't argue that. I do argue it's a hack compared to the principled solution of a bottom type. -- Andrei
Jul 09 2017
On 7/10/17 12:02 AM, Nicholas Wilson wrote:So far I count four requirements of a solution: documentation optimisation ability to statically reflect upon ability to implement Of the approached listed only out{assert(0);} fails the static reflection check.I'm pretty much giving up on arguing in this thread, as it seems people are really keen to add more bloat to the language in spite of the obvious bloat-free solution. But I have to ask, what is the benefit of statically determining that a function is noreturn? I thought it was just a hint to the compiler that some optimizations can be done for that call? -Steve
Jul 10 2017
On 7/10/2017 4:00 AM, Steven Schveighoffer wrote:But I have to ask, what is the benefit of statically determining that a function is noreturn?It is useful anywhere dataflow analysis is useful. FunctionThatDoesnotReturn(); a = b; // error: unreachable code
Jul 10 2017
On 7/10/17 2:38 PM, Walter Bright wrote:On 7/10/2017 4:00 AM, Steven Schveighoffer wrote:That doesn't require static introspection. The compiler can see that, the user doesn't have to worry about it. Note, one thing that hasn't been mentioned is how we are now going to be able to make regular functions noreturn. This means templates that accept aliases will now have to deal with the possibility that those aliases are noreturn. So if the compiler, for instance, marks the above as an error, what happens here? foo(alias f)() { f(); auto a = b; } if f is a noreturn function, is this instantiation an error? That's going to get messy. It's not a problem today, because you can't alias `assert`, and the compiler doesn't care about other functions that don't return (even if they are completely available). I learned the hard way with inout not to proactively make obvious errors errors. For instance, you can't return inout if you don't have any inout parameters. It makes complete logical sense when you think about it, just use const. This leads to the (inout int = 0) crap we see everywhere in phobos. -SteveBut I have to ask, what is the benefit of statically determining that a function is noreturn?It is useful anywhere dataflow analysis is useful. FunctionThatDoesnotReturn(); a = b; // error: unreachable code
Jul 10 2017
On Monday, 10 July 2017 at 18:50:54 UTC, Steven Schveighoffer wrote:On 7/10/17 2:38 PM, Walter Bright wrote:Currently not. This is either a bug or the compiler's flow analysis is not robust enough to detect this case. int b; auto foo(alias f)() { f(); auto a = b; //no unreachable code error } void main() { foo!(() => assert(0))(); }On 7/10/2017 4:00 AM, Steven Schveighoffer wrote:That doesn't require static introspection. The compiler can see that, the user doesn't have to worry about it. Note, one thing that hasn't been mentioned is how we are now going to be able to make regular functions noreturn. This means templates that accept aliases will now have to deal with the possibility that those aliases are noreturn. So if the compiler, for instance, marks the above as an error, what happens here? foo(alias f)() { f(); auto a = b; } if f is a noreturn function, is this instantiation an error?But I have to ask, what is the benefit of statically determining that a function is noreturn?It is useful anywhere dataflow analysis is useful. FunctionThatDoesnotReturn(); a = b; // error: unreachable code
Jul 10 2017
On 7/10/2017 12:05 PM, Meta wrote:Currently not. This is either a bug or the compiler's flow analysis is not robust enough to detect this case.Flow analysis relies on the function's signature, not its implementation, and currently the signature contains no information about noreturn. Addressing this is the whole point of this thread.
Jul 10 2017
On 7/10/17 3:05 PM, Meta wrote:On Monday, 10 July 2017 at 18:50:54 UTC, Steven Schveighoffer wrote:I think the implication from Walter is that f would be treated just like a direct call to assert(0) (i.e. it doesn't return). So where code like this produces an "unreachable statement" error: void foo() { assert(0); auto a = b; } with dmd -w, code like the above will potentially produce an unreachable code error if f is a noreturn (the compiler can now do dataflow analysis and determine it's unreachable). This means that you get errors for some instantiations. Which ironically means you'd need to do something like this: void foo(alias f)() { f(); static if(!isNoreturn!f) { auto a = 5; ...// etc. } } Today, it's not a big issue. We can't alias `assert` function directly, so it's obvious where it's an assert or not (because you have to spell it out!). We have similar problems today with generic code causing unreachability errors. See for instance https://issues.dlang.org/show_bug.cgi?id=14835 -SteveOn 7/10/17 2:38 PM, Walter Bright wrote:Currently not. This is either a bug or the compiler's flow analysis is not robust enough to detect this case.On 7/10/2017 4:00 AM, Steven Schveighoffer wrote:That doesn't require static introspection. The compiler can see that, the user doesn't have to worry about it. Note, one thing that hasn't been mentioned is how we are now going to be able to make regular functions noreturn. This means templates that accept aliases will now have to deal with the possibility that those aliases are noreturn. So if the compiler, for instance, marks the above as an error, what happens here? foo(alias f)() { f(); auto a = b; } if f is a noreturn function, is this instantiation an error?But I have to ask, what is the benefit of statically determining that a function is noreturn?It is useful anywhere dataflow analysis is useful. FunctionThatDoesnotReturn(); a = b; // error: unreachable code
Jul 10 2017
On Monday, 10 July 2017 at 20:00:10 UTC, Steven Schveighoffer wrote:This means that you get errors for some instantiations. Which ironically means you'd need to do something like this: void foo(alias f)() { f(); static if(!isNoreturn!f) { auto a = 5; ...// etc. } } Today, it's not a big issue. We can't alias `assert` function directly, so it's obvious where it's an assert or not (because you have to spell it out!). We have similar problems today with generic code causing unreachability errors. See for instance https://issues.dlang.org/show_bug.cgi?id=14835 -SteveI think that for our own sanity the dead-code check would have to be disabled in this case. I agree that it'd be awful putting `static if (!isNoReturn!f)` everywhere. Actually, why not just disable it entirely in templated functions?
Jul 11 2017
On Monday, 10 July 2017 at 04:02:59 UTC, Nicholas Wilson wrote:1) noreturn 2) disable(return) 3)none w.r.t optimisation assuming both 1 & 3 impact DMD equally [...]I don't think that's true. A Bottom type does not cover all use cases of noreturn/ pragma(noreturn). Example 1: Polymorphism class Bird { void fly() { ... } }; class Penguin : Bird { override void fly() pragma(noreturn) { assert(0); } }; class EvolvedPenguin : Penguin { override void fly() { ... } }; There's no way to encode that information in a return type. Example 2: Compile-time polymorphism Same as above, except during compile time. While it looks ok in theory (just return Bottom, and everything is alright), it seems very tricky to get right. Example from checkedint.d: auto r = hook.hookOpUnary!op(payload); return Checked!(typeof(r), Hook)(r); Let's say the hook refuses to perform hookOpUnary, so r is Bottom. Unfortunately, Checked!(Bottom, Hook)(r) doesn't compile (because "if (isIntegral!T || is(T == Checked!(U, H), U, H))" fails). While Bottom may be substituted into all expressions (which doesn't seem easy anyway), it for sure can't be inserted as any template argument. As seen before, Checked is not Bottom-proof. I would think that most templates are not Bottom-proof and making them Bottom-proof seems quite a bit of work. With pragma(noreturn) that situation would be no problem. Example 3: Unreachable statements/Implicit noreturn inference As pointed out by Steven Schveighoffer, the current unreachability errors should probably be removed in generic code. If we do that, then generic functions can be pragma(noreturn) if certain conditions are met. A compiler can easily figure that out, but writing it inside static ifs could be almost impossible. Assume we have a hook to Checked that disallows casts. Current signature: U opCast(U, this _)() if (isIntegral!U || isFloatingPoint!U || is(U == bool)) The compiler can figure out that all code paths end with an pragma(noreturn), so it can add that pragma implicitly to the signature. However, the compiler can't change the return type from U to Bottom (otherwise static equality checks with U will fail).
Jul 14 2017
On Friday, 14 July 2017 at 15:39:01 UTC, Guillaume Boucher wrote:Example 1: Polymorphism class Bird { void fly() { ... } }; class Penguin : Bird { override void fly() pragma(noreturn) { assert(0); } }; class EvolvedPenguin : Penguin { override void fly() { ... } };No matter how you look at it, this code should simply not be allowed: Bird bird = ...; bird.fly(); // is this return or noreturn? Penguin penguin = ...; penguin.fly(); // is this return or noreturn? In both cases, compiler cannot draw any conclusions about return/noreturn and thus I believe such code should not be allowed. And if this is disallowed, a Bottom type would fit again.
Jul 14 2017
On 07/14/2017 03:06 PM, Lurker wrote:On Friday, 14 July 2017 at 15:39:01 UTC, Guillaume Boucher wrote:Conventional thinking has it that derived methods should "require less and deliver more" such that substitution is possible. That's where contravariant parameters and covariant returns come from. Therefore, methods that do not return should be able to override those that return. (The opposite is unworkable btw.) Note that the absence of a "noreturn" annotation does not imply a guarantee that the method does return. AndreiExample 1: Polymorphism class Bird { void fly() { ... } }; class Penguin : Bird { override void fly() pragma(noreturn) { assert(0); } }; class EvolvedPenguin : Penguin { override void fly() { ... } };No matter how you look at it, this code should simply not be allowed: Bird bird = ...; bird.fly(); // is this return or noreturn? Penguin penguin = ...; penguin.fly(); // is this return or noreturn? In both cases, compiler cannot draw any conclusions about return/noreturn and thus I believe such code should not be allowed.
Jul 14 2017
On 14.07.2017 17:39, Guillaume Boucher wrote:On Monday, 10 July 2017 at 04:02:59 UTC, Nicholas Wilson wrote:I think it does, but it is a significantly more invasive language change.1) noreturn 2) disable(return) 3)none w.r.t optimisation assuming both 1 & 3 impact DMD equally [...]I don't think that's true. A Bottom type does not cover all use cases of noreturn/ pragma(noreturn). ...Example 1: Polymorphism class Bird { void fly() { ... } }; class Penguin : Bird { override void fly() pragma(noreturn) { assert(0); } }; class EvolvedPenguin : Penguin { override void fly() { ... } }; There's no way to encode that information in a return type. ...I'd say a function with return type Bottom can override any function in the base class.Example 2: Compile-time polymorphism Same as above, except during compile time. While it looks ok in theory (just return Bottom, and everything is alright), it seems very tricky to get right. Example from checkedint.d: auto r = hook.hookOpUnary!op(payload); return Checked!(typeof(r), Hook)(r); Let's say the hook refuses to perform hookOpUnary, so r is Bottom. Unfortunately, Checked!(Bottom, Hook)(r) doesn't compile (because "if (isIntegral!T || is(T == Checked!(U, H), U, H))" fails). While Bottom may be substituted into all expressions (which doesn't seem easy anyway), it for sure can't be inserted as any template argument. As seen before, Checked is not Bottom-proof. I would think that most templates are not Bottom-proof and making them Bottom-proof seems quite a bit of work. ...The problem for this example is that the current implementation of isIntegral would return false for Bottom.With pragma(noreturn) that situation would be no problem. ...pragma(noreturn) is indeed the simpler solution, as it does not interact with anything else. The fact that template constraints in some cases need to be aware of the existence of Bottom in order to work for Bottom is clearly a negative property of this solution in the context of D.Example 3: Unreachable statements/Implicit noreturn inference As pointed out by Steven Schveighoffer, the current unreachability errors should probably be removed in generic code. If we do that, then generic functions can be pragma(noreturn) if certain conditions are met. A compiler can easily figure that out, but writing it inside static ifs could be almost impossible. Assume we have a hook to Checked that disallows casts. Current signature: U opCast(U, this _)() if (isIntegral!U || isFloatingPoint!U || is(U == bool)) The compiler can figure out that all code paths end with an pragma(noreturn), so it can add that pragma implicitly to the signature. However, the compiler can't change the return type from U to Bottom (otherwise static equality checks with U will fail).You can return 'auto' instead of U. Then the return type will be inferred either as U or Bottom.
Jul 16 2017
On Sunday, 16 July 2017 at 13:03:40 UTC, Timon Gehr wrote:The best you can hope for is that any code with pragma(noreturn) can be rewritten into functionally equivalent code that uses Bottom and gives the same hints to the optimizer. pragma(noreturn) can be inferred implicitly which makes it more impactful in practice.I don't think that's true. A Bottom type does not cover all use cases of noreturn/ pragma(noreturn). ...I think it does, but it is a significantly more invasive language change.I'd say a function with return type Bottom can override any function in the base class.That solves the "Penguin : Bird" step, but not "EvolvedPenguin : Penguin" (which can fly). Andrei argues that my example don't comply with a puristic understanding of inheritance. Maybe that's enough of a reason to not optimize such use cases, but it still shows that pragma(noreturn) is somehow stronger than Bottom.pragma(noreturn) is indeed the simpler solution, as it does not interact with anything else. The fact that template constraints in some cases need to be aware of the existence of Bottom in order to work for Bottom is clearly a negative property of this solution in the context of D.Yes, basically this.You can return 'auto' instead of U. Then the return type will be inferred either as U or Bottom.Sure there are workarounds. Also here: auto deref(T)(ref T* x) { return deref(*x); } ref T deref(T)(ref T x) if (!isPointer!T) { return x; } But when every small function needs a rewrite, something seems off.
Jul 16 2017
On 16.07.2017 21:49, Guillaume Boucher wrote:The issue isn't purism, it is type safety. If you create an EvolvedPenguin, upcast it to a Penguin and call the fly method you get UB. So noreturn would indeed need to enforce that all overrides are also noreturn.I'd say a function with return type Bottom can override any function in the base class.That solves the "Penguin : Bird" step, but not "EvolvedPenguin : Penguin" (which can fly). Andrei argues that my example don't comply with a puristic understanding of inheritance. Maybe that's enough of a reason to not optimize such use cases, but it still shows that pragma(noreturn) is somehow stronger than Bottom.
Jul 16 2017
On Sunday, 16 July 2017 at 20:04:25 UTC, Timon Gehr wrote:The issue isn't purism, it is type safety. If you create an EvolvedPenguin, upcast it to a Penguin and call the fly method you get UB. So noreturn would indeed need to enforce that all overrides are also noreturn.I see it as some kind of weak guarantee, where the compiler can assume noreturn only if he knows there are no subtypes involved (e.g. if he's applying devirtualization). Automatically inheriting the attribute could break existing code (especially if it's inferred).
Jul 16 2017
On 7/16/2017 6:03 AM, Timon Gehr wrote:pragma(noreturn) is indeed the simpler solution, as it does not interact with anything else.Pragmas are not supposed to change the semantics of the code, they are intended as directions to the compiler, such as: * optimization suggestions * embedding instructions into the object file * sending messages to the user while compiling * instructing the compiler on special name mangling etc. A pragma(noreturn) influences semantics, and so is inappropriate. That's what attributes and types are for.
Jul 19 2017
On 2017-07-09 23:14, H. S. Teoh via Digitalmars-d wrote:I like out{ assert(0); } for pretty much the same reasons as Steven lists. The biggest pro is that **the language already supports it*. Contract syntax already is meant to signal intent, and assert(0) signals "never gets here". You can't find a better solution than this.I highly doubt that the compiler does not need to be changed at all to get the wanted behavior. It that case it's just as easy to add a compiler recognized UDA. It doesn't add any more baggage than "out{ assert(0); }". With that said, I don't care that much at all and I'm questioning how useful this feature is to have at all. -- /Jacob Carlborg
Jul 10 2017
On Mon, Jul 10, 2017 at 03:47:35PM +0200, Jacob Carlborg via Digitalmars-d wrote:On 2017-07-09 23:14, H. S. Teoh via Digitalmars-d wrote:I did not say the compiler does not need to be changed. I said the *language* does not need to be changed.I like out{ assert(0); } for pretty much the same reasons as Steven lists. The biggest pro is that **the language already supports it*. Contract syntax already is meant to signal intent, and assert(0) signals "never gets here". You can't find a better solution than this.I highly doubt that the compiler does not need to be changed at all to get the wanted behavior.It that case it's just as easy to add a compiler recognized UDA. It doesn't add any more baggage than "out{ assert(0); }". With that said, I don't care that much at all and I'm questioning how useful this feature is to have at all.[...] As is usual around here, things of marginal consequence receive the most attention and energy spent in debating every last nitpick. Sigh. T -- Unix was not designed to stop people from doing stupid things, because that would also stop them from doing clever things. -- Doug Gwyn
Jul 10 2017
On Sunday, 9 July 2017 at 11:26:27 UTC, Steven Schveighoffer wrote:The one disadvantage, is that -release removes contracts. So in this particular case, the out contract should remain.Doesn't the compiler know about an out contract even with -release? I don't understand why -release would prevent the relevant information from being passed to the compiler.
Jul 09 2017
On 07/09/2017 06:51 AM, Daniel N wrote:On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Too indirect and verbose. An attribute or special "return type" would just cut straight to the case.On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 09 2017
On Sun, Jul 09, 2017 at 02:49:35PM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:On 07/09/2017 06:51 AM, Daniel N wrote:If DIP 1009 is accepted, this would not be verbose at all: void func() out(false) // it even tells you it won't return { ... } Plus, this has the advantage that you can specify a valid return type for when you need to override a base class method, e.g.: class JitCompiler { Code compile(string code); } class InvalidCompiler : JitCompiler { override Code compile(string code) out(false) {...} } A Bottom type that implicitly converts to anything would also fit the bill, granted, but does require changing the language. The main thing I have against adding a Bottom type is that it adds yet another built-in type to the language, which means (1) a combinatorial explosion of existing language constructs combined with the new type, with currently-unknown consequences (and possibly corner cases we haven't thought of that will come back to bite us), (2) yet another special type newcomers have to learn with special semantics (I can already anticipate complaints to D.learn about what's the difference between null and bottom); (3) having to implement lots of compiler changes to handle how this new type interacts with operators and other types in all possible cases; (4) all of this just for something that (a) is only rarely used, and (b) could have been easily implemented by adding noreturn or using existing contract syntax without adding a whole new basic type to the language. Honestly, I'd vote for noreturn as the simplest, most straightforward solution, and the only reason I'm arguing for out{assert(0);} (or out(false) if DIP 1009 is accepted) is because people are all up in arms about adding Gosh Yet Another Special UDA In Addition To The Numerous Special UDAs We Already Have Like safe and nogc. T -- The diminished 7th chord is the most flexible and fear-instilling chord. Use it often, use it unsparingly, to subdue your listeners into submission!On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Too indirect and verbose. An attribute or special "return type" would just cut straight to the case.On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 10 2017
On Monday, 10 July 2017 at 18:09:54 UTC, H. S. Teoh wrote:On Sun, Jul 09, 2017 at 02:49:35PM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:+1On 07/09/2017 06:51 AM, Daniel N wrote:If DIP 1009 is accepted, this would not be verbose at all: void func() out(false) // it even tells you it won't return { ... } Plus, this has the advantage that you can specify a valid return type for when you need to override a base class method, e.g.: class JitCompiler { Code compile(string code); } class InvalidCompiler : JitCompiler { override Code compile(string code) out(false) {...} } A Bottom type that implicitly converts to anything would also fit the bill, granted, but does require changing the language. The main thing I have against adding a Bottom type is that it adds yet another built-in type to the language, which means (1) a combinatorial explosion of existing language constructs combined with the new type, with currently-unknown consequences (and possibly corner cases we haven't thought of that will come back to bite us), (2) yet another special type newcomers have to learn with special semantics (I can already anticipate complaints to D.learn about what's the difference between null and bottom); (3) having to implement lots of compiler changes to handle how this new type interacts with operators and other types in all possible cases; (4) all of this just for something that (a) is only rarely used, and (b) could have been easily implemented by adding noreturn or using existing contract syntax without adding a whole new basic type to the language. Honestly, I'd vote for noreturn as the simplest, most straightforward solution, and the only reason I'm arguing for out{assert(0);} (or out(false) if DIP 1009 is accepted) is because people are all up in arms about adding Gosh Yet Another Special UDA In Addition To The Numerous Special UDAs We Already Have Like safe and nogc.On Sunday, 9 July 2017 at 10:31:47 UTC, Mr.D wrote:Too indirect and verbose. An attribute or special "return type" would just cut straight to the case.On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:void func() out { assert(0); } body { }Has anyone a better idea?What about scope(exit) assert(0); ?
Jul 10 2017
On 7/9/2017 3:31 AM, Mr.D wrote:scope(exit) assert(0);It needs to be part of the function signature, not the function implementation.
Jul 09 2017
On 2017-07-08 12:15, Walter Bright wrote:C compilers (and by extension C++ compilers) usually have an extension which allows a function to be marked as one that never returns. The point of this is it enables improved data flow analysis and better code being generated. Noreturn functions crop up in things like assert's and enforce's. DMD internally hardcodes a few functions it knows about that are noreturn, and the DMD optimizer and codegen take advantage of it. But when people write their own assert's and enforce's, this falls apart. While the programs will still work, they won't be as efficient as they could be.I'm going to ask the stupid question: I understand that this is for the compiler to generate better code. But, since the attribute (or whatever it is) indicates that a function won't return, it can only be used in very few cases. Are those few cases worth optimizing for? -- /Jacob Carlborg
Jul 10 2017
Jacob Carlborg wrote:I'm going to ask the stupid question: I understand that this is for the compiler to generate better code. But, since the attribute (or whatever it is) indicates that a function won't return, it can only be used in very few cases. Are those few cases worth optimizing for?the case that makes noreturn worth having is even not optimizing, but don't adding visual noise at the call site. case Smth: error("boo"); case Other: ... oops. i know that `error()` will never return, but compiler doesn't, and insisting on adding `break;` there. sure, i can either put break, or always put `assert(0);` after noreturn functions, but hey, aren't we invented computers exactly to lay off such borning things onto them!? ;-)
Jul 10 2017
On 7/10/2017 12:02 PM, Jacob Carlborg wrote:Are those few cases worth optimizing for?Yes. Not having it makes enforce(), for example, generate poor code.
Jul 10 2017
On 2017-07-10 22:00, Walter Bright wrote:Yes. Not having it makes enforce(), for example, generate poor code.Not sure I understand. "enforce" will return if the condition is true. -- /Jacob Carlborg
Jul 10 2017
On 11 July 2017 at 08:46, Jacob Carlborg via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 2017-07-10 22:00, Walter Bright wrote:Right, you can't put noreturn on enforce itself, but you can on the internal bailOut function called by enforce.Yes. Not having it makes enforce(), for example, generate poor code.Not sure I understand. "enforce" will return if the condition is true.
Jul 11 2017
On 2017-07-11 09:37, Iain Buclaw via Digitalmars-d wrote:Right, you can't put noreturn on enforce itself, but you can on the internal bailOut function called by enforce.Ah, but I though it would just contain a "throw" for the case the the condition is false. I see now that it doesn't. -- /Jacob Carlborg
Jul 11 2017
Am Sat, 8 Jul 2017 03:15:39 -0700 schrieb Walter Bright <newshound2 digitalmars.com>:[=E2=80=A6] Having an noreturn attribute will take care of that: =20 noreturn void ThisFunctionExits(); =20 Yes, it's another builtin attribute and attributes are arguably a failure=in=20language design.The 'none' return type sounds interesting, because a noreturn function is also a void function, it is practical to implement this as a void sub-type or compiler recognized druntime defined "type intrinsic". On the other hand, the attribute solution has worked well for the existing compilers in practice and such a rarely used tag doesn't add significantly to the meticulous D programmers list: "pure safe nothrow nogc". --=20 Marco
Jul 10 2017
On 08.07.2017 12:15, Walter Bright wrote:C compilers (and by extension C++ compilers) usually have an extension which allows a function to be marked as one that never returns. The point of this is it enables improved data flow analysis and better code being generated. Noreturn functions crop up in things like assert's and enforce's. DMD internally hardcodes a few functions it knows about that are noreturn, and the DMD optimizer and codegen take advantage of it. But when people write their own assert's and enforce's, this falls apart. While the programs will still work, they won't be as efficient as they could be. Having an noreturn attribute will take care of that: noreturn void ThisFunctionExits(); Yes, it's another builtin attribute and attributes are arguably a failure in language design.I don't like the inflation of attributes, too, but encoding it into the return type seems too clever, especially when having to explain it again and again to people without functional language background (like me). A few questions inspired from discussion in https://github.com/dlang/druntime/pull/1839: - Does noreturn need to be inferred for templates? void msgAssert(bool cond)(string msg) { debug writeln(msg); assert(cond); } - The template function above is (strongly) pure, how does noreturn interact with that? Will noreturn ensure it is not elided? - If it does, how does this propagate to a function calling it, e.g. void c_assert(bool cond, string msg) pure { if (!cond) msgAssert!false(msg); } A call to c_assert can be elided according to the pure rules. Any chance that this can be avoided with the help of noreturn?
Jul 11 2017