www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - proposed noreturn attribute

reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling next sibling parent reply bachmeier <no spam.net> writes:
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
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 7/8/17 7:07 AM, bachmeier wrote:
 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?
So it's part of the summary of the function. -- Andrei
Jul 08
next sibling parent bachmeier <no spam.net> writes:
On Saturday, 8 July 2017 at 12:18:38 UTC, Andrei Alexandrescu 
wrote:
 On 7/8/17 7:07 AM, bachmeier wrote:
 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?
So it's part of the summary of the function. -- Andrei
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.
Jul 08
prev sibling parent Random D user <no email.com> writes:
On Saturday, 8 July 2017 at 12:18:38 UTC, Andrei Alexandrescu 
wrote:
 On 7/8/17 7:07 AM, bachmeier wrote:
 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?
So it's part of the summary of the function. -- Andrei
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 :|
Jul 11
prev sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
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
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/08/2017 01:14 PM, Jack Stouffer wrote:
 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.
If present in the signature of the function you can figure that the function won't return by introspection. -- Andrei
Jul 08
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/8/2017 10:14 AM, Jack Stouffer wrote:
 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.
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.
Jul 08
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
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
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 8 July 2017 at 12:17:57 UTC, Andrei Alexandrescu 
wrote:
 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
... 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).
Jul 08
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 7/8/17 1:30 PM, Stefan Koch wrote:
 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.
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.
 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
prev sibling next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Saturday, 8 July 2017 at 12:17:57 UTC, Andrei Alexandrescu 
wrote:
 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
I wonder if some lessons from Haskell's "bottom" type would be relevant here.
Jul 08
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
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
parent "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 07/08/2017 02:37 PM, Andrei Alexandrescu wrote:
 nice touch of bottom (heh)
Where's D's PC-police now?
Jul 09
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 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!
[...] 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.
Jul 08
parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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
parent Lurker <lurker gmail.com> writes:
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.


 T
The reverse is also true, and more disastrous!
Jul 13
prev sibling next sibling parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
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
next sibling parent Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
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
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/08/2017 05:03 PM, Vladimir Panteleev wrote:
 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.
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. Andrei
Jul 08
prev sibling next sibling parent reply Martin Nowak <code dawg.eu> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent crimaniak <crimaniak gmail.com> writes:
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
prev sibling parent reply sarn <sarn theartofmachinery.com> writes:
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
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/8/2017 7:01 PM, sarn wrote:
 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.
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.)
Jul 08
next sibling parent reply Meta <jared771 gmail.com> writes:
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
next sibling parent reply Meta <jared771 gmail.com> writes:
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:
 (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.
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/8/2017 9:32 PM, Meta wrote:
 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:
 (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 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.
 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;`).
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.
Jul 08
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09.07.2017 07:44, Walter Bright wrote:
 On 7/8/2017 9:32 PM, Meta wrote:
 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:
 (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 read that a Void function in Haskell does not return. Is that incorrect?
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.)
 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
parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/9/2017 3:48 AM, Timon Gehr wrote:
 It is indeed incorrect, [...]
Thanks!
Jul 09
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/8/2017 9:23 PM, Meta wrote:
 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.).
Thanks for the explanation.
 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
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 7/9/17 1:36 AM, Walter Bright wrote:
 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!
We should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null). -- Andrei
Jul 09
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 9 July 2017 at 13:13:28 UTC, Andrei Alexandrescu wrote:
 On 7/9/17 1:36 AM, Walter Bright wrote:
 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!
We should use typeof(assert(0)) for Bottom. There is precedent - there is no name for typeof(null). -- Andrei
I agree with is approach. It does not add a magic Type :)
Jul 09
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 9 July 2017 at 19:12:45 UTC, Walter Bright wrote:
 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?
Not valid. Cannot take a pointer of typeof(assert(0)).
Jul 09
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/09/2017 03:12 PM, Walter Bright wrote:
 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?
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. Andrei
Jul 09
parent reply Meta <jared771 gmail.com> writes:
On Sunday, 9 July 2017 at 22:28:50 UTC, Andrei Alexandrescu wrote:
 On 07/09/2017 03:12 PM, Walter Bright wrote:
 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?
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. Andrei
`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)`.
Jul 09
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/09/2017 07:34 PM, Meta wrote:
 On Sunday, 9 July 2017 at 22:28:50 UTC, Andrei Alexandrescu wrote:
 On 07/09/2017 03:12 PM, Walter Bright wrote:
 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?
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. Andrei
`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). -- Andrei
Jul 09
parent reply Meta <jared771 gmail.com> writes:
On Monday, 10 July 2017 at 00:34:54 UTC, Andrei Alexandrescu 
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)`.
Agreed (with the mention it's not in contradiction with the above). -- Andrei
That was in response to:
 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.
My 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.
Jul 10
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
next sibling parent reply Meta <jared771 gmail.com> writes:
On Tuesday, 11 July 2017 at 08:29:12 UTC, Timon Gehr wrote:
 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.
It's a regular type with unusual behaviour due to it being uninhabited.
Jul 11
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11.07.2017 19:27, Meta wrote:
 On Tuesday, 11 July 2017 at 08:29:12 UTC, Timon Gehr wrote:
 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.
It's a regular type with unusual behaviour due to it being uninhabited.
All types are unusual. That does not mean they are not all types.
Jul 12
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 7/11/17 4:29 AM, Timon Gehr wrote:
 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.
Wouldn't the fact that it's a subtype of all other types make it a bit more peculiar? -- Andrei
Jul 11
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12.07.2017 03:50, Andrei Alexandrescu wrote:
 On 7/11/17 4:29 AM, Timon Gehr wrote:
 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.
Wouldn't the fact that it's a subtype of all other types make it a bit more peculiar? -- Andrei
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.
Jul 12
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent Timon Gehr <timon.gehr gmx.ch> writes:
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) -> B
I 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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent Olivier FAURE <olivier.faure epitech.eu> writes:
On Sunday, 9 July 2017 at 19:14:37 UTC, Walter Bright wrote:
 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));
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.
Jul 10
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/08/2017 10:25 PM, Walter Bright wrote:
 On 7/8/2017 7:01 PM, sarn wrote:
 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.
Thanks, it looks like we won't get any help from type theory. We're on our own :-)
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. Andrei
Jul 08
prev sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 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?
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. :-D
 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
prev sibling parent "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 07/08/2017 08:17 AM, Andrei Alexandrescu wrote:
 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.
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. That would be my #1 top preference: Your suggestion here, but just make "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.
Jul 09
prev sibling next sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
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
prev sibling next sibling parent reply Mr.D <4d.eniz.zz gmail.com> writes:
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
next sibling parent reply Daniel N <no public.email> writes:
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:

 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Jul 09
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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:
 On Saturday, 8 July 2017 at 10:15:39 UTC, Walter Bright wrote:

 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Yes, this! -Steve
Jul 09
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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:
 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:

 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Yes, this! -Steve
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.
Jul 09
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/9/17 7:07 AM, Nicholas Wilson wrote:
 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:
 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:

 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Yes, this!
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.
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. -Steve
Jul 09
next sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 9 July 2017 at 11:26:27 UTC, Steven Schveighoffer 
wrote:
 On 7/9/17 7:07 AM, Nicholas Wilson wrote:
 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:
 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:

 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Yes, this!
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.
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. -Steve
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.
Jul 09
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 7/9/17 7:26 AM, Steven Schveighoffer wrote:
 On 7/9/17 7:07 AM, Nicholas Wilson wrote:
 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:
 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:

 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Yes, this!
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.
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.
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. -- Andrei
Jul 09
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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. -- Andrei
 
Your 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
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/09/2017 10:10 AM, Steven Schveighoffer wrote:
 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. -- Andrei
Your definition of exotic differs from mine.
Mine is closest to: "strikingly, excitingly, or mysteriously different or unusual" :o) in https://www.merriam-webster.com/dictionary/exotic -- Andrei
Jul 09
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On Sunday, 9 July 2017 at 14:43:20 UTC, Andrei Alexandrescu wrote:
 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.
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. -Steve
Jul 09
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 9 July 2017 at 18:01:08 UTC, Andrei Alexandrescu wrote:
 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
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.
Jul 09
prev sibling next sibling parent reply Meta <jared771 gmail.com> writes:
On Sunday, 9 July 2017 at 18:01:08 UTC, Andrei Alexandrescu wrote:
 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
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; }
Jul 09
next sibling parent reply Meta <jared771 gmail.com> writes:
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; //ditto
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.
Jul 09
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Meta <jared771 gmail.com> writes:
On Sunday, 9 July 2017 at 21:32:31 UTC, Walter Bright wrote:
 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`?
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.
Jul 09
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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 that
Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
Jul 12
next sibling parent reply Meta <jared771 gmail.com> writes:
On Wednesday, 12 July 2017 at 09:32:32 UTC, Timon Gehr wrote:
 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 that
Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
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.
Jul 12
parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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
parent Meta <jared771 gmail.com> writes:
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:
 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
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 #![feature(never_type)] directive: fn test<T>() {} fn main() { test::<!>(); } So it seems my Rust knowledge is about a year out of date.
Jul 19
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/12/2017 05:32 AM, Timon Gehr wrote:
 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 that
Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
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.
 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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On Wednesday, 12 July 2017 at 14:23:15 UTC, Andrei Alexandrescu 
wrote:
 On 07/12/2017 05:32 AM, Timon Gehr wrote:
 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 that
Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
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. ...
I might do that, however there are a couple of open questions (see below).
 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". ...
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.
 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.
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.
 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
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 7/13/17 2:37 PM, Timon Gehr wrote:
 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.)
I wonder if sizeof could be made size_t.max. -- Andrei
Jul 13
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/13/2017 5:18 PM, Andrei Alexandrescu wrote:
 On 7/13/17 2:37 PM, Timon Gehr wrote:
 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.)
I wonder if sizeof could be made size_t.max. -- Andrei
I thought bottom.sizeof would be 0.
Jul 15
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 16.07.2017 05:30, Walter Bright wrote:
 On 7/13/2017 5:18 PM, Andrei Alexandrescu wrote:
 On 7/13/17 2:37 PM, Timon Gehr wrote:
 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.)
I wonder if sizeof could be made size_t.max. -- Andrei
I thought bottom.sizeof would be 0.
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.
Jul 16
next sibling parent reply Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 16.07.2017 21:49, Guillaume Boucher wrote:
 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).
In this case, Bottom.sizeof is a value of type Bottom, which must not exist.
Jul 16
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/16/2017 04:15 PM, Timon Gehr wrote:
 On 16.07.2017 21:49, Guillaume Boucher wrote:
 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).
In this case, Bottom.sizeof is a value of type Bottom, which must not exist.
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! Andrei
Jul 16
prev sibling next sibling parent Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
On Sunday, 16 July 2017 at 20:15:11 UTC, Timon Gehr wrote:
 On 16.07.2017 21:49, Guillaume Boucher wrote:
 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).
In this case, Bottom.sizeof is a value of type Bottom, which must not exist.
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.
Jul 16
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/17/2017 5:13 PM, Timon Gehr wrote:
 (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 ∞.
Infinity makes sense for 1/0, but I don't see how that automatically transfers to size_t.
Jul 17
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 18.07.2017 03:06, Walter Bright wrote:
 On 7/17/2017 5:13 PM, Timon Gehr wrote:
 (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 ∞.
Infinity makes sense for 1/0, but I don't see how that automatically transfers to size_t.
It does not. The reasoning is unrelated.
Jul 17
prev sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Monday, 17 July 2017 at 23:01:40 UTC, Walter Bright wrote:
 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.)
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?
Jul 18
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
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
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 18 July 2017 at 12:15:06 UTC, Timon Gehr wrote:
 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.
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 ?
Jul 18
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 18.07.2017 14:19, Stefan Koch wrote:
 On Tuesday, 18 July 2017 at 12:15:06 UTC, Timon Gehr wrote:
 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.
Timon, how important is it to actually have bottom ?
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.)
 ... 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
parent reply Yuxuan Shui <yshuiv7 gmail.com> writes:
On Tuesday, 18 July 2017 at 15:26:59 UTC, Timon Gehr wrote:
 On 18.07.2017 14:19, Stefan Koch 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.) [...]
What about void?
Jul 18
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 18.07.2017 20:46, Yuxuan Shui wrote:
 On Tuesday, 18 July 2017 at 15:26:59 UTC, Timon Gehr wrote:
 On 18.07.2017 14:19, Stefan Koch 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.) [...]
What about void?
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;
Jul 18
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 18 July 2017 at 20:49:56 UTC, Timon Gehr wrote:
 On 18.07.2017 20:46, Yuxuan Shui wrote:
 On Tuesday, 18 July 2017 at 15:26:59 UTC, Timon Gehr wrote:
 On 18.07.2017 14:19, Stefan Koch 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.) [...]
What about void?
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();
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?
Jul 18
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
parent Moritz Maxeiner <moritz ucworks.org> writes:
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:
 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).
That's a good pragmatic (syntactic sugar) reason, thanks.
Jul 18
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/15/2017 11:30 PM, Walter Bright wrote:
 On 7/13/2017 5:18 PM, Andrei Alexandrescu wrote:
 On 7/13/17 2:37 PM, Timon Gehr wrote:
 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.)
I wonder if sizeof could be made size_t.max. -- Andrei
I thought bottom.sizeof would be 0.
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. -- Andrei
Jul 16
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/13/2017 01:25 PM, Timon Gehr wrote:
 On Wednesday, 12 July 2017 at 14:23:15 UTC, Andrei Alexandrescu wrote:
 On 07/12/2017 05:32 AM, Timon Gehr wrote:
 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 that
Sorry, but this thinking has no place in type system design. This is precisely how you create a convoluted nonsensical mess.
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. ...
I might do that, however there are a couple of open questions (see below).
Sorry missed those... let's see:
 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
next sibling parent reply Olivier FAURE <olivier.faure epitech.eu> writes:
On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu 
wrote:
 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'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()?".
Jul 17
next sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
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:
 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'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()?".
Yes, this!
Jul 17
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
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
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 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.
[...] 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.
Jul 17
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
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:
 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.
[...] 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
+1000! You've said it all!
Jul 17
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
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
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 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
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 N
Jul 17
next sibling parent reply Seb <seb wilzba.ch> writes:
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
parent Brad Roberts via Digitalmars-d <digitalmars-d puremagic.com> writes:
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-314910297
Semi-valid, but <insert rational and well written paragraph on the false fungability of time and effort>.
Jul 17
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
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
prev sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/18/2017 5:54 PM, Nicholas Wilson wrote:
 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.
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.
 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.
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.
Jul 18
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
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.
 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*.
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 :-)
Jul 18
prev sibling next sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
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:
 [...]
[...] 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: [...]
object.d: alias noreturn = typeof(assert(0)); Atila
Jul 18
prev sibling parent Olivier FAURE <olivier.faure epitech.eu> writes:
On Monday, 17 July 2017 at 18:10:27 UTC, Andrei Alexandrescu 
wrote:
 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.
Fair enough.
Jul 18
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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:
 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.
It's the bottom. Bottom is to Types, as Object is to Classes.
Jul 19
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 19.07.2017 12:35, Stefan Koch wrote:
 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:
 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.
It's the bottom. Bottom is to Types, as Object is to Classes.
No. Bottom is to types as typeof(null) is to class types.
Jul 19
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 19.07.2017 12:59, Stefan Koch wrote:
 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 ?
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.
Jul 19
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
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
next sibling parent Marco Leise <Marco.Leise gmx.de> writes:
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:
 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).
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. -- Marco
Jul 19
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 19.07.2017 14:13, Moritz Maxeiner wrote:
 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).
https://en.wikipedia.org/wiki/Principle_of_explosion
Jul 19
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 19 July 2017 at 14:32:24 UTC, Timon Gehr wrote:
 On 19.07.2017 14:13, Moritz Maxeiner wrote:
 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).
https://en.wikipedia.org/wiki/Principle_of_explosion
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.
Jul 19
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 19.07.2017 16:47, Moritz Maxeiner wrote:
 On Wednesday, 19 July 2017 at 14:32:24 UTC, Timon Gehr wrote:
 On 19.07.2017 14:13, Moritz Maxeiner wrote:
 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).
https://en.wikipedia.org/wiki/Principle_of_explosion
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.
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.
Jul 19
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 19 July 2017 at 14:52:28 UTC, Timon Gehr wrote:
 On 19.07.2017 16:47, Moritz Maxeiner wrote:
 On Wednesday, 19 July 2017 at 14:32:24 UTC, Timon Gehr wrote:
 On 19.07.2017 14:13, Moritz Maxeiner wrote:
 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).
https://en.wikipedia.org/wiki/Principle_of_explosion
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.
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.
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).
Jul 19
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 19.07.2017 12:24, Marc Schütz wrote:
 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.
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.
Jul 19
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/09/2017 03:30 PM, Meta wrote:
 alias Bottom = typeof(assert(0)); //for convenience
Speaking 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
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/09/2017 06:32 PM, Andrei Alexandrescu wrote:
 On 07/09/2017 03:30 PM, Meta wrote:
 alias Bottom = typeof(assert(0)); //for convenience
Speaking 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
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. Andrei
Jul 09
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On Sunday, 9 July 2017 at 18:01:08 UTC, Andrei Alexandrescu wrote:
 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.
I would argue a type hack in the compiler doesn't compare to the principaled use of existing syntax and semantics ;) -Steve
Jul 09
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 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
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.
Jul 09
next sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
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:
 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
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".
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.
Jul 09
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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:
 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:
 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
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".
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.
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.
Jul 09
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/10/17 2:38 PM, Walter Bright wrote:
 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
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. -Steve
Jul 10
parent reply Meta <jared771 gmail.com> writes:
On Monday, 10 July 2017 at 18:50:54 UTC, Steven Schveighoffer 
wrote:
 On 7/10/17 2:38 PM, Walter Bright wrote:
 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
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?
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))(); }
Jul 10
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/10/17 3:05 PM, Meta wrote:
 On Monday, 10 July 2017 at 18:50:54 UTC, Steven Schveighoffer wrote:
 On 7/10/17 2:38 PM, Walter Bright wrote:
 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
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?
Currently not. This is either a bug or the compiler's flow analysis is not robust enough to detect this case.
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 -Steve
Jul 10
parent Meta <jared771 gmail.com> writes:
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

 -Steve
I 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
prev sibling parent reply Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
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
next sibling parent reply Lurker <lurker gmail.com> writes:
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
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/14/2017 03:06 PM, Lurker wrote:
 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.
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. Andrei
Jul 14
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 14.07.2017 17:39, Guillaume Boucher wrote:
 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). ...
I think it does, but it is a significantly more invasive language change.
 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
parent reply Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
On Sunday, 16 July 2017 at 13:03:40 UTC, Timon Gehr wrote:
 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.
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'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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 16.07.2017 21:49, Guillaume Boucher wrote:
 
 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.
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.
Jul 16
parent Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
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
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
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
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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 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.
I did not say the compiler does not need to be changed. I said the *language* does not need to be changed.
 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
prev sibling parent bachmeier <no spam.net> writes:
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
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 07/09/2017 06:51 AM, Daniel N wrote:
 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:

 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Too indirect and verbose. An attribute or special "return type" would just cut straight to the case.
Jul 09
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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:
 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:
 
 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Too indirect and verbose. An attribute or special "return type" would just cut straight to the case.
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!
Jul 10
parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
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:
 On 07/09/2017 06:51 AM, Daniel N wrote:
 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:
 
 Has anyone a better idea?
What about scope(exit) assert(0); ?
void func() out { assert(0); } body { }
Too indirect and verbose. An attribute or special "return type" would just cut straight to the case.
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.
+1
Jul 10
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
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
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Jacob Carlborg <doob me.com> writes:
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
parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:

 Yes. Not having it makes enforce(), for example, generate poor code.
Not sure I understand. "enforce" will return if the condition is true.
Right, you can't put noreturn on enforce itself, but you can on the internal bailOut function called by enforce.
Jul 11
parent Jacob Carlborg <doob me.com> writes:
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
prev sibling next sibling parent Marco Leise <Marco.Leise gmx.de> writes:
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=20
 language 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
prev sibling parent Rainer Schuetze <r.sagitario gmx.de> writes:
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