digitalmars.D - bottom type as parameter or local variable, does that make sense?
- WebFreak001 (39/39) Jan 14 2022 For example:
- H. S. Teoh (10/18) Jan 14 2022 [...]
- vit (13/30) Jan 14 2022 Whith explicit init it works like that:
- H. S. Teoh (8/42) Jan 14 2022 IMO, that's a bug. Implicit initialization of noreturn should behave
- bauss (2/44) Jan 14 2022 Why is it even allowed, that's beyond my understanding?
- Nick Treleaven (3/4) Jan 14 2022 So that generic code compiles when a type resolves to noreturn:
- Paul Backus (18/59) Jan 14 2022 That's not what the bug is here. `noreturn` is the type of
- H. S. Teoh (16/37) Jan 14 2022 But in D, whenever a variable is declared, it gets initialized to its
- Timon Gehr (4/9) Jan 14 2022 I don't follow why this is necessarily desirable. E.g., if generic code
- H. S. Teoh (10/21) Jan 14 2022 ? Isn't that exactly what it should be? If generic code checks whether
- Timon Gehr (5/29) Jan 14 2022 My point was the opposite of what you now seem to be taking it to be.
- Tejas (8/49) Jan 15 2022 Not to put words in HS's mouth, but I think the reason why simply
- Timon Gehr (6/16) Jan 15 2022 Maybe, but why is it more desirable to pretend that the empty type can
- Steven Schveighoffer (6/14) Jan 15 2022 But noreturn consumes no space. Even if it's not the equivalent of ` =
- Max Samukha (4/10) Jan 14 2022 Then, should "noreturn x = noreturn.init;" fail? In my world, it
- Paul Backus (12/22) Jan 14 2022 My point is, they're not equivalent.
- Max Samukha (7/12) Jan 15 2022 I understand the argument and counterarguments. I don't know what
- Paul Backus (13/25) Jan 15 2022 Making initialization of `noreturn` fail at runtime would require
- Max Samukha (6/20) Jan 15 2022 noreturn is no more special than 0 number or empty set. They are
- Paul Backus (4/9) Jan 15 2022 It can't. There are no values of type `noreturn`. The set of all
- Max Samukha (5/8) Jan 15 2022 Right, but if we want to strictly follow the definitions, then we
- Paul Backus (15/23) Jan 15 2022 Technically, a variable is a name associated with a block of
- Max Samukha (9/23) Jan 16 2022 Memory of size 0 contains a single value, which is the value of
- Paul Backus (25/49) Jan 16 2022 Unit type = size 0, 1 possible value.
- H. S. Teoh (24/53) Jan 16 2022 [...]
- Timon Gehr (22/50) Jan 17 2022 I share your confusion. A block of memory of size zero indeed has a
- Elronnd (5/9) Jan 17 2022 I don't think this is right; it must be that A.sizeof==B.sizeof.
- H. S. Teoh (6/17) Jan 17 2022 This is exactly what happens when you pass a derived class to a function
- Elronnd (2/5) Jan 17 2022 You can't do pointer arithmetic on class instances.
- H. S. Teoh (5/12) Jan 17 2022 Sure you can. Just cast it to void* and arithmetic away.
- Timon Gehr (29/39) Jan 17 2022 Well, that clearly can't be the case, unless you want to claim that
- Paul Backus (8/15) Jan 17 2022 I think the best way to describe the behavior specified by the
- H. S. Teoh (8/27) Jan 17 2022 The question then would be, why such a special case? There's no need to
- Paul Backus (4/22) Jan 17 2022 Both the rewrite I describe and the behavior you propose are
- H. S. Teoh (5/28) Jan 17 2022 OK, so it should be a compile-time error, then.
- Paul Backus (7/14) Jan 17 2022 ...and then you need special cases in generic code to deal with
- H. S. Teoh (19/35) Jan 17 2022 What kind of special cases, specifically? Making variables of type
- Timon Gehr (2/18) Jan 17 2022 +1. Exactly what I was wondering about too.
- Timon Gehr (8/10) Jan 17 2022 Variables of type `noreturn` should certainly remain legal, perhaps just...
- H. S. Teoh (11/25) Jan 18 2022 Hmm. This again treads that default-initialization / manual
- Timon Gehr (9/38) Jan 18 2022 I am not creating any values, I am taking them from the caller.
- Max Samukha (3/59) Feb 05 2022 Thanks, Timon! Don't construe the lack of a reply as disrespect.
- Timon Gehr (6/37) Jan 14 2022 Debatable, but I think you have misidentified the questionable
- Steven Schveighoffer (21/69) Jan 14 2022 I have several thoughts here:
- Nick Treleaven (9/14) Jan 14 2022 I made a PR to document noreturn with an example of a function
For example: ```d import std.stdio; void foo(noreturn a) { writeln("we got called!"); } void bar() { noreturn b; writeln("calling"); foo(b); } void main() { writeln("bar"); bar(); writeln("done"); } ``` Guess what it prints and reply to it on this thread. I think the result is pretty nonsensical. Reveal: https://run.dlang.io/is/4SXQal Should this usage be allowed? I would say not allowing it would make more sense. Additionally if we don't allow it we could allow aliasing to it like: ```d deprecated("OldType is no longer supported, use NewType instead") alias OldType = noreturn; ``` and with that both add hint messages for users to migrate and not allow broken code to compile. A sample for this would be libdparse, where the arguments of the visit functions determine which function is called, even if the argument isn't actually used or just ignored. The cases where the argument type is specified, but not used, do happen and migrating types there to force the user to rewrite code is currently not possible without completely removing the type.
Jan 14 2022
On Fri, Jan 14, 2022 at 11:52:04AM +0000, WebFreak001 via Digitalmars-d wrote:```d[...]void bar() { noreturn b; writeln("calling"); foo(b); }[...]```Wait, doesn't the DIP say that while declaring a variable of type `noreturn` is technically allowed, it should abort at runtime as soon as the variable is initialized? Why is program actually running past that line??! T -- It only takes one twig to burn down a forest.
Jan 14 2022
On Friday, 14 January 2022 at 13:58:38 UTC, H. S. Teoh wrote:On Fri, Jan 14, 2022 at 11:52:04AM +0000, WebFreak001 via Digitalmars-d wrote:Whith explicit init it works like that: ```d import std.stdio; void main(){ noreturn n = noreturn.init; writeln("bar"); } ``` Print: ``` //Illegal instruction (core dumped) ``````d[...]void bar() { noreturn b; writeln("calling"); foo(b); }[...]```Wait, doesn't the DIP say that while declaring a variable of type `noreturn` is technically allowed, it should abort at runtime as soon as the variable is initialized? Why is program actually running past that line??! T
Jan 14 2022
On Fri, Jan 14, 2022 at 02:05:25PM +0000, vit via Digitalmars-d wrote:On Friday, 14 January 2022 at 13:58:38 UTC, H. S. Teoh wrote:[...]On Fri, Jan 14, 2022 at 11:52:04AM +0000, WebFreak001 via Digitalmars-d wrote:```d[...]void bar() { noreturn b; writeln("calling"); foo(b); }[...]```Wait, doesn't the DIP say that while declaring a variable of type `noreturn` is technically allowed, it should abort at runtime as soon as the variable is initialized? Why is program actually running past that line??!Whith explicit init it works like that: ```d import std.stdio; void main(){ noreturn n = noreturn.init; writeln("bar"); } ``` Print: ``` //Illegal instruction (core dumped) ```IMO, that's a bug. Implicit initialization of noreturn should behave exactly the same way as invoking noreturn.init explicitly. Somebody should file a bug, if one hasn't been filed already. T -- We are in class, we are supposed to be learning, we have a teacher... Is it too much that I expect him to teach me??? -- RL
Jan 14 2022
On Friday, 14 January 2022 at 15:07:13 UTC, H. S. Teoh wrote:On Fri, Jan 14, 2022 at 02:05:25PM +0000, vit via Digitalmars-d wrote:Why is it even allowed, that's beyond my understanding?On Friday, 14 January 2022 at 13:58:38 UTC, H. S. Teoh wrote:[...]On Fri, Jan 14, 2022 at 11:52:04AM +0000, WebFreak001 via Digitalmars-d wrote:```d[...]void bar() { noreturn b; writeln("calling"); foo(b); }[...]```Wait, doesn't the DIP say that while declaring a variable of type `noreturn` is technically allowed, it should abort at runtime as soon as the variable is initialized? Why is program actually running past that line??!Whith explicit init it works like that: ```d import std.stdio; void main(){ noreturn n = noreturn.init; writeln("bar"); } ``` Print: ``` //Illegal instruction (core dumped) ```IMO, that's a bug. Implicit initialization of noreturn should behave exactly the same way as invoking noreturn.init explicitly. Somebody should file a bug, if one hasn't been filed already. T
Jan 14 2022
On Friday, 14 January 2022 at 15:55:54 UTC, bauss wrote:Why is it even allowed, that's beyond my understanding?So that generic code compiles when a type resolves to noreturn: https://github.com/dlang/DIPs/blob/15081980cd393e21218da6836321ed37ebc48dd3/DIPs/DIP1034.md#interaction-with-other-language-features
Jan 14 2022
On Friday, 14 January 2022 at 15:07:13 UTC, H. S. Teoh wrote:On Fri, Jan 14, 2022 at 02:05:25PM +0000, vit via Digitalmars-d wrote:That's not what the bug is here. `noreturn` is the type of expressions whose evaluation does not halt; i.e., it is a type with no values. Therefore, declaring a variable of type `noreturn` is a no-op: all it does is add a symbol to the current scope. No initialization is performed, because there is nothing to initialize. On the other hand, this also means that any attempt to *evaluate* a `noreturn` *expression* (such as `noreturn.init`) can never succeed. So, a function call expression with a `noreturn` argument can never result in the function actually being called: it must either enter an infinite loop, throw an exception, terminate the program, etc. Some `noreturn` expressions, like `throw new Exception` and `() { while(1) {} }()`, already had specific runtime behaviors defined by the language spec. Others, like `noreturn.init`, did not. For the latter kind of `noreturn` expression, the DIP specifies that their behavior at runtime is equivalent to `assert(0)`.On Friday, 14 January 2022 at 13:58:38 UTC, H. S. Teoh wrote:[...]On Fri, Jan 14, 2022 at 11:52:04AM +0000, WebFreak001 via Digitalmars-d wrote: [...]```d void bar() { noreturn b; writeln("calling"); foo(b); } ```[...] Wait, doesn't the DIP say that while declaring a variable of type `noreturn` is technically allowed, it should abort at runtime as soon as the variable is initialized? Why is program actually running past that line??!Whith explicit init it works like that: ```d import std.stdio; void main(){ noreturn n = noreturn.init; writeln("bar"); } ``` Print: ``` //Illegal instruction (core dumped) ```IMO, that's a bug. Implicit initialization of noreturn should behave exactly the same way as invoking noreturn.init explicitly. Somebody should file a bug, if one hasn't been filed already.
Jan 14 2022
On Fri, Jan 14, 2022 at 04:32:22PM +0000, Paul Backus via Digitalmars-d wrote:On Friday, 14 January 2022 at 15:07:13 UTC, H. S. Teoh wrote:[...]But in D, whenever a variable is declared, it gets initialized to its default value unless specified otherwise. Since nothing is specified here, it ought to perform its default initialization, and since there is no value with which it can be initialized, it ought to raise a runtime exception.IMO, that's a bug. Implicit initialization of noreturn should behave exactly the same way as invoking noreturn.init explicitly. Somebody should file a bug, if one hasn't been filed already.That's not what the bug is here. `noreturn` is the type of expressions whose evaluation does not halt; i.e., it is a type with no values. Therefore, declaring a variable of type `noreturn` is a no-op: all it does is add a symbol to the current scope. No initialization is performed, because there is nothing to initialize.On the other hand, this also means that any attempt to *evaluate* a `noreturn` *expression* (such as `noreturn.init`) can never succeed. So, a function call expression with a `noreturn` argument can never result in the function actually being called: it must either enter an infinite loop, throw an exception, terminate the program, etc.In this case, the function never gets called, just as you said. But neither does it enter an infinite loop, throw an exception, or terminate the program. So IMO there's definitely a bug here.Some `noreturn` expressions, like `throw new Exception` and `() { while(1) {} }()`, already had specific runtime behaviors defined by the language spec. Others, like `noreturn.init`, did not. For the latter kind of `noreturn` expression, the DIP specifies that their behavior at runtime is equivalent to `assert(0)`.The `b` in `foo(b)` is an expression; the act of evaluating that expression (in the course of calling foo) ought to terminate the program with an assert(0). But it currently doesn't. So it must be a bug. T -- Philosophy: how to make a career out of daydreaming.
Jan 14 2022
On 1/14/22 18:41, H. S. Teoh wrote:But in D, whenever a variable is declared, it gets initialized to its default value unless specified otherwise. Since nothing is specified here, it ought to perform its default initialization, and since there is no value with which it can be initialized, it ought to raise a runtime exception.I don't follow why this is necessarily desirable. E.g., if generic code checks whether or not a type can be default constructed, it will by default get a spurious "yes" answer and has to special case `noreturn`.
Jan 14 2022
On Fri, Jan 14, 2022 at 07:24:22PM +0100, Timon Gehr via Digitalmars-d wrote:On 1/14/22 18:41, H. S. Teoh wrote:? Isn't that exactly what it should be? If generic code checks whether type T can be default constructed, and T happens to be noreturn, then it would fail because noreturn.init is not constructible. Presumably, the code is doing this check because it's about to create a variable of type T afterwards, so returning "yes" would lead to instantiating noreturn variables. Why would that be a good thing? T -- The best way to destroy a cause is to defend it poorly.But in D, whenever a variable is declared, it gets initialized to its default value unless specified otherwise. Since nothing is specified here, it ought to perform its default initialization, and since there is no value with which it can be initialized, it ought to raise a runtime exception.I don't follow why this is necessarily desirable. E.g., if generic code checks whether or not a type can be default constructed, it will by default get a spurious "yes" answer and has to special case `noreturn`.
Jan 14 2022
On 1/14/22 19:32, H. S. Teoh wrote:On Fri, Jan 14, 2022 at 07:24:22PM +0100, Timon Gehr via Digitalmars-d wrote:noreturn.init is assert(0), which compiles.On 1/14/22 18:41, H. S. Teoh wrote:? Isn't that exactly what it should be? If generic code checks whether type T can be default constructed, and T happens to be noreturn, then it would fail because noreturn.init is not constructible.But in D, whenever a variable is declared, it gets initialized to its default value unless specified otherwise. Since nothing is specified here, it ought to perform its default initialization, and since there is no value with which it can be initialized, it ought to raise a runtime exception.I don't follow why this is necessarily desirable. E.g., if generic code checks whether or not a type can be default constructed, it will by default get a spurious "yes" answer and has to special case `noreturn`.Presumably, the code is doing this check because it's about to create a variable of type T afterwards, so returning "yes" would lead to instantiating noreturn variables. Why would that be a good thing? TMy point was the opposite of what you now seem to be taking it to be. You said there ought to be default initialization, which for noreturn is a runtime exception. I asked why this "ought" to be so.
Jan 14 2022
On Friday, 14 January 2022 at 20:59:18 UTC, Timon Gehr wrote:On 1/14/22 19:32, H. S. Teoh wrote:Not to put words in HS's mouth, but I think the reason why simply declaring the `noreturn` variable should lead to initialisation is because that's how _all_ variable declarations in D behave, unless you disable default initialisation, at which point it's a compiler error to simply declare the variable. `noreturn` not default initializing is therefore a special case, which I feel is undesirableOn Fri, Jan 14, 2022 at 07:24:22PM +0100, Timon Gehr via Digitalmars-d wrote:noreturn.init is assert(0), which compiles.On 1/14/22 18:41, H. S. Teoh wrote:? Isn't that exactly what it should be? If generic code checks whether type T can be default constructed, and T happens to be noreturn, then it would fail because noreturn.init is not constructible.But in D, whenever a variable is declared, it gets initialized to its default value unless specified otherwise. Since nothing is specified here, it ought to perform its default initialization, and since there is no value with which it can be initialized, it ought to raise a runtime exception.I don't follow why this is necessarily desirable. E.g., if generic code checks whether or not a type can be default constructed, it will by default get a spurious "yes" answer and has to special case `noreturn`.Presumably, the code is doing this check because it's about to create a variable of type T afterwards, so returning "yes" would lead to instantiating noreturn variables. Why would that be a good thing? TMy point was the opposite of what you now seem to be taking it to be. You said there ought to be default initialization, which for noreturn is a runtime exception. I asked why this "ought" to be so.
Jan 15 2022
On 15.01.22 12:02, Tejas wrote:Exactly. Unless.Not to put words in HS's mouth, but I think the reason why simply declaring the `noreturn` variable should lead to initialisation is because that's how _all_ variable declarations in D behave, unless you disable default initialisation, at which point it's a compiler error to simply declare the variable. ...`noreturn` not default initializing is therefore a special case, which I feel is undesirableMaybe, but why is it more desirable to pretend that the empty type can be default-initialized? You can still declare noreturn variables with an explicit initializer, but it seems a bit shady to make `assert(0)` the default of anything.
Jan 15 2022
On 1/15/22 6:02 AM, Tejas wrote:Not to put words in HS's mouth, but I think the reason why simply declaring the `noreturn` variable should lead to initialisation is because that's how _all_ variable declarations in D behave, unless you disable default initialisation, at which point it's a compiler error to simply declare the variable. `noreturn` not default initializing is therefore a special case, which I feel is undesirableBut noreturn consumes no space. Even if it's not the equivalent of ` = noreturn.init`, it's actually initialized just by declaring it. I feel the odd thing here is the existance of noreturn.init *at all*. The reason it does exist is quite simple though -- generic code. -Steve
Jan 15 2022
On Friday, 14 January 2022 at 16:32:22 UTC, Paul Backus wrote:That's not what the bug is here. `noreturn` is the type of expressions whose evaluation does not halt; i.e., it is a type with no values. Therefore, declaring a variable of type `noreturn` is a no-op: all it does is add a symbol to the current scope. No initialization is performed, because there is nothing to initialize.Then, should "noreturn x = noreturn.init;" fail? In my world, it is equivalent to "noreturn x;". There is still an implicit initialization, which should halt.
Jan 14 2022
On Friday, 14 January 2022 at 22:55:37 UTC, Max Samukha wrote:On Friday, 14 January 2022 at 16:32:22 UTC, Paul Backus wrote:My point is, they're not equivalent. * Execution of `noreturn x = noreturn.init;` requires evaluating the *expression* `noreturn.init`. * Execution of `noreturn x;` does not require evaluating anything. Yes, this is an exception to the general rule that `T x = T.init` and `T x;` are equivalent. Why does this exception exist? Because for any type `T` other than `noreturn`, the expression `T.init` evaluates to a value of type `T`. However, the expression `noreturn.init` does not evaluate to a value of type `noreturn`--because, as previously stated, *there is no such thing as a value of type `noreturn`*.That's not what the bug is here. `noreturn` is the type of expressions whose evaluation does not halt; i.e., it is a type with no values. Therefore, declaring a variable of type `noreturn` is a no-op: all it does is add a symbol to the current scope. No initialization is performed, because there is nothing to initialize.Then, should "noreturn x = noreturn.init;" fail? In my world, it is equivalent to "noreturn x;". There is still an implicit initialization, which should halt.
Jan 14 2022
On Saturday, 15 January 2022 at 00:54:03 UTC, Paul Backus wrote:Why does this exception exist? Because for any type `T` other than `noreturn`, the expression `T.init` evaluates to a value of type `T`. However, the expression `noreturn.init` does not evaluate to a value of type `noreturn`--because, as previously stated, *there is no such thing as a value of type `noreturn`*.I understand the argument and counterarguments. I don't know what is the right way. I err on the side of not introducing the special case: every variable in D requires initialization, and initialization of noreturn should fail because the initializer halts. You argue that noreturn doesn't have a value, so we don't even need to evaluate the initializer. Tight corner.
Jan 15 2022
On Saturday, 15 January 2022 at 09:52:00 UTC, Max Samukha wrote:On Saturday, 15 January 2022 at 00:54:03 UTC, Paul Backus wrote:Making initialization of `noreturn` fail at runtime would require *two* special cases: first, the special case of `noreturn` having no values when every other type in D has at least one value; and second, the special case of `noreturn`'s default initializer not evaluating to a `noreturn` value when every other type's default initializer evaluates to a value of that type. There is an argument to be made that we should never have `noreturn` DIP should have been rejected). But given that we have, I think it is better to accept the consequences that follow from that decision, strange though they may seem, than to introduce even more special cases to try and paper over them.Why does this exception exist? Because for any type `T` other than `noreturn`, the expression `T.init` evaluates to a value of type `T`. However, the expression `noreturn.init` does not evaluate to a value of type `noreturn`--because, as previously stated, *there is no such thing as a value of type `noreturn`*.I understand the argument and counterarguments. I don't know what is the right way. I err on the side of not introducing the special case: every variable in D requires initialization, and initialization of noreturn should fail because the initializer halts. You argue that noreturn doesn't have a value, so we don't even need to evaluate the initializer. Tight corner.
Jan 15 2022
On Saturday, 15 January 2022 at 14:43:14 UTC, Paul Backus wrote:Making initialization of `noreturn` fail at runtime would require *two* special cases: first, the special case of `noreturn` having no values when every other type in D has at least one value;noreturn is no more special than 0 number or empty set. They are special cases of the right kind. Divide by zero fails at runtime, but removing 0 because of that would be a bad idea.and second, the special case of `noreturn`'s default initializer not evaluating to a `noreturn` value when every other type's default initializer evaluates to a value of that type.I think noreturn.init can be viewed as a value of noreturn.There is an argument to be made that we should never have `noreturn` DIP should have been rejected). But given that we have, I think it is better to accept the consequences that follow from that decision, strange though they may seem, than to introduce even more special cases to try and paper over them.I am not convinced that your special case is better than mine. )
Jan 15 2022
On Saturday, 15 January 2022 at 16:34:55 UTC, Max Samukha wrote:It can't. There are no values of type `noreturn`. The set of all `noreturn` values is the empty set. That is literally the definition of a bottom type.and second, the special case of `noreturn`'s default initializer not evaluating to a `noreturn` value when every other type's default initializer evaluates to a value of that type.I think noreturn.init can be viewed as a value of noreturn.
Jan 15 2022
On Saturday, 15 January 2022 at 16:55:49 UTC, Paul Backus wrote:It can't. There are no values of type `noreturn`. The set of all `noreturn` values is the empty set. That is literally the definition of a bottom type.Right, but if we want to strictly follow the definitions, then we can't declare a variable of the empty type, because the definition of a variable is literally a name associated with a value, so we are back to where we started.
Jan 15 2022
On Saturday, 15 January 2022 at 17:57:02 UTC, Max Samukha wrote:On Saturday, 15 January 2022 at 16:55:49 UTC, Paul Backus wrote:Technically, a variable is a name associated with a block of memory, which may or may not contain a value. In the case of `noreturn`, the block of memory has size 0, and there are no possible values it can contain. So, technically, a `noreturn` variable is impossible to initialize. But since the compiler knows that the behavior of the program can't possibly depend on the variable's value, it is free to just skip the initialization altogether and leave the variable uninitialized instead. I guess you could make an argument that you should have to write `noreturn x = void;` every time you declare a `noreturn` variable. But even then, it would not be correct for `noreturn x;` to result in an `assert(0)` at runtime--it would have to be a *compile-time* error.It can't. There are no values of type `noreturn`. The set of all `noreturn` values is the empty set. That is literally the definition of a bottom type.Right, but if we want to strictly follow the definitions, then we can't declare a variable of the empty type, because the definition of a variable is literally a name associated with a value, so we are back to where we started.
Jan 15 2022
On Saturday, 15 January 2022 at 18:35:02 UTC, Paul Backus wrote:Technically, a variable is a name associated with a block of memory, which may or may not contain a value. In the case of `noreturn`, the block of memory has size 0, and there are no possible values it can contain.Memory of size 0 contains a single value, which is the value of the unit type, 0-tuple, or whatever. Zero type is different. Timon, I am totally confused, could you clarify?So, technically, a `noreturn` variable is impossible to initialize. But since the compiler knows that the behavior of the program can't possibly depend on the variable's value, it is free to just skip the initialization altogether and leave the variable uninitialized instead.Sounds like "alias x = noreturn.init;"I guess you could make an argument that you should have to write `noreturn x = void;` every time you declare a `noreturn` variable. But even then, it would not be correct for `noreturn x;` to result in an `assert(0)` at runtime--it would have to be a *compile-time* error.I think it depends on the definition of local declaration. For statics, it is obviously a compile-time error. For locals, my feeling is it should be a runtime error, but who knows. Timon, help.
Jan 16 2022
On Sunday, 16 January 2022 at 22:56:44 UTC, Max Samukha wrote:On Saturday, 15 January 2022 at 18:35:02 UTC, Paul Backus wrote:Unit type = size 0, 1 possible value. Bottom type = size 0, 0 possible values. The reason there are 0 possible values is because it is the bottom type, not because its size is 0.Technically, a variable is a name associated with a block of memory, which may or may not contain a value. In the case of `noreturn`, the block of memory has size 0, and there are no possible values it can contain.Memory of size 0 contains a single value, which is the value of the unit type, 0-tuple, or whatever. Zero type is different. Timon, I am totally confused, could you clarify?You can't `alias` an expression, so the above is ill-formed.So, technically, a `noreturn` variable is impossible to initialize. But since the compiler knows that the behavior of the program can't possibly depend on the variable's value, it is free to just skip the initialization altogether and leave the variable uninitialized instead.Sounds like "alias x = noreturn.init;"It would be a compile-time error for essentially the same reason that default-initializing a type with a disabled default constructor is a compile-time error: you have asked the compiler to do something that it knows, at compile time, is impossible to do. I think maybe the misconception that is leading you astray here is that you assume that "default-initializing a variable of type `T`" requires "evaluating the expression `T.init`". It does not. What default-initializing a variable of type `T` requires is 1. Determining the in-memory representation of `T`'s default value. 2. Arranging for that representation to be placed in the variable's memory at the start of its lifetime. In the case of `noreturn`, the in-memory representation is "nothing", because `noreturn` has no default value (nor any other values), and arranging for that representation to be placed in the appropriate memory is either impossible or a no-op (depending on how you view things), because there is nothing to place and no memory to place it in.I guess you could make an argument that you should have to write `noreturn x = void;` every time you declare a `noreturn` variable. But even then, it would not be correct for `noreturn x;` to result in an `assert(0)` at runtime--it would have to be a *compile-time* error.I think it depends on the definition of local declaration. For statics, it is obviously a compile-time error. For locals, my feeling is it should be a runtime error, but who knows. Timon, help.
Jan 16 2022
On Mon, Jan 17, 2022 at 12:32:37AM +0000, Paul Backus via Digitalmars-d wrote: [...]Unit type = size 0, 1 possible value. Bottom type = size 0, 0 possible values. The reason there are 0 possible values is because it is the bottom type, not because its size is 0.[...]On Sunday, 16 January 2022 at 22:56:44 UTC, Max Samukha wrote:[...]In this case, the bottom type not only has a disabled default ctor, it *cannot* have any ctors (because it has no values). It can never be constructed.I think it depends on the definition of local declaration. For statics, it is obviously a compile-time error. For locals, my feeling is it should be a runtime error, but who knows. Timon, help.It would be a compile-time error for essentially the same reason that default-initializing a type with a disabled default constructor is a compile-time error: you have asked the compiler to do something that it knows, at compile time, is impossible to do.I think maybe the misconception that is leading you astray here is that you assume that "default-initializing a variable of type `T`" requires "evaluating the expression `T.init`". It does not. What default-initializing a variable of type `T` requires is 1. Determining the in-memory representation of `T`'s default value. 2. Arranging for that representation to be placed in the variable's memory at the start of its lifetime.IMO this is conflating the implementation of default initialization and the conceptual semantics of declaring a variable at the language level. In my mind, a variable of some type T is box that stores values of type T. A variable declaration without an initializer is simply a box that holds the default value of T. Since noreturn has no values, it also doesn't have a default value, so such a box cannot exist. A box that holds something that cannot exist is a contradiction; i.e., declaring a variable of type noreturn is a logic error that should either cause a compilation error or abort at runtime.In the case of `noreturn`, the in-memory representation is "nothing", because `noreturn` has no default value (nor any other values), and arranging for that representation to be placed in the appropriate memory is either impossible or a no-op (depending on how you view things), because there is nothing to place and no memory to place it in.The in-memory representation doesn't exist, because noreturn has no values. This is different from something that has a zero-size representation (i.e., a unit type). It's a contradiction to be in a situation where such a representation is needed (regardless of its size), because it can't exist. So IMO it should be impossible. T -- Век живи - век учись. А дураком помрёшь.
Jan 16 2022
On 16.01.22 23:56, Max Samukha wrote:On Saturday, 15 January 2022 at 18:35:02 UTC, Paul Backus wrote:I share your confusion. A block of memory of size zero indeed has a unique value, not zero values. I don't think there is any principled way to arrive at a semantics where the program survives an attempt to initialize a `noreturn` variable. Also, note that typically, if a type `A*` is a subtype of a type `B*`, then `A.sizeof>=B.sizeof`. `noreturn*` is a subtype of any `T*`. Hence, `noreturn.sizeof` should be at least `size_t.max` or even `∞`. Thus, `noreturn.sizeof == 0` is already a special case with a pragmatic justification and using it to attempt to justify any further behavior of `noreturn` is questionable in the first place.Technically, a variable is a name associated with a block of memory, which may or may not contain a value. In the case of `noreturn`, the block of memory has size 0, and there are no possible values it can contain.Memory of size 0 contains a single value, which is the value of the unit type, 0-tuple, or whatever. Zero type is different. Timon, I am totally confused, could you clarify? ...In D, types that do not have a default constructor cannot be default-constructed. The question is whether `noreturn`, the empty type, should really have a default constructor (that immediately terminates the program). As far as I understand, according to the DIP, `noreturn` has a default constructor, but `noreturn` variables are initialized lazily when they are accessed. I am not sure whether that's pragmatically the right choice, because it's a special case and generic code might check whether some type is default-constructible and only in this case declare a local variable of that type. At least it crashes at the latest possible moment, I guess. It's certainly a bit weird that now, in D, 0·x is not always 0.So, technically, a `noreturn` variable is impossible to initialize. But since the compiler knows that the behavior of the program can't possibly depend on the variable's value, it is free to just skip the initialization altogether and leave the variable uninitialized instead.Sounds like "alias x = noreturn.init;"I guess you could make an argument that you should have to write `noreturn x = void;` every time you declare a `noreturn` variable. But even then, it would not be correct for `noreturn x;` to result in an `assert(0)` at runtime--it would have to be a *compile-time* error.I think it depends on the definition of local declaration. For statics, it is obviously a compile-time error. For locals, my feeling is it should be a runtime error, but who knows. Timon, help.
Jan 17 2022
On Monday, 17 January 2022 at 16:11:03 UTC, Timon Gehr wrote:if a type `A*` is a subtype of a type `B*`, then `A.sizeof>=B.sizeof`. `noreturn*` is a subtype of any `T*`. Hence, `noreturn.sizeof` should be at least `size_t.max` or even `∞`.I don't think this is right; it must be that A.sizeof==B.sizeof. Consider e.g.: B* f(B* b) { return b+1; } What happens if you pass in an A*, and A.sizeof>B.sizeof?
Jan 17 2022
On Mon, Jan 17, 2022 at 05:53:25PM +0000, Elronnd via Digitalmars-d wrote:On Monday, 17 January 2022 at 16:11:03 UTC, Timon Gehr wrote:This is exactly what happens when you pass a derived class to a function that expects the base class, BTW. T -- The problem with the world is that everybody else is stupid.if a type `A*` is a subtype of a type `B*`, then `A.sizeof>=B.sizeof`. `noreturn*` is a subtype of any `T*`. Hence, `noreturn.sizeof` should be at least `size_t.max` or even `∞`.I don't think this is right; it must be that A.sizeof==B.sizeof. Consider e.g.: B* f(B* b) { return b+1; } What happens if you pass in an A*, and A.sizeof>B.sizeof?
Jan 17 2022
On Monday, 17 January 2022 at 18:05:49 UTC, H. S. Teoh wrote:You can't do pointer arithmetic on class instances.What happens if you pass in an A*, and A.sizeof>B.sizeof?This is exactly what happens when you pass a derived class to a function that expects the base class, BTW.
Jan 17 2022
On Mon, Jan 17, 2022 at 06:32:19PM +0000, Elronnd via Digitalmars-d wrote:On Monday, 17 January 2022 at 18:05:49 UTC, H. S. Teoh wrote:Sure you can. Just cast it to void* and arithmetic away. T -- One Word to write them all, One Access to find them, One Excel to count them all, And thus to Windows bind them. -- Mike ChampionYou can't do pointer arithmetic on class instances.What happens if you pass in an A*, and A.sizeof>B.sizeof?This is exactly what happens when you pass a derived class to a function that expects the base class, BTW.
Jan 17 2022
On 1/17/22 18:53, Elronnd wrote:On Monday, 17 January 2022 at 16:11:03 UTC, Timon Gehr wrote:Well, that clearly can't be the case, unless you want to claim that `int.sizeof==noreturn.sizeof==long.sizeof`: ```d void main(){ noreturn* null_; int* ip = null_; long* lp = null_; class C{ int* foo(){ return null_; } } class D:C{ override noreturn* foo(){ return null_; } } int* f(int* b){ return b+1; } long* g(long* b){ return b+1; } f(null_); g(null_); } ``` In any case, to the question "how big is a value that can never be allocated", "∞" is a natural answer, even if you don't buy the derivation over subtyping.if a type `A*` is a subtype of a type `B*`, then `A.sizeof>=B.sizeof`. `noreturn*` is a subtype of any `T*`. Hence, `noreturn.sizeof` should be at least `size_t.max` or even `∞`.I don't think this is right; it must be that A.sizeof==B.sizeof.B* f(B* b) { return b+1; } What happens if you pass in an A*, and A.sizeof>B.sizeof?Just another reason why pointer arithmetic is unsafe. E.g., see what happens in C++, where class references are explicitly typed as pointers: ```c++ #include <iostream> using namespace std; struct B{ int x; }; struct A:B{ int y=123; }; B* f(B* b){ return b+1; } int main(){ cout<<f(new A)->x<<'\n'; } // "123" ```
Jan 17 2022
On Monday, 17 January 2022 at 16:11:03 UTC, Timon Gehr wrote:In D, types that do not have a default constructor cannot be default-constructed. The question is whether `noreturn`, the empty type, should really have a default constructor (that immediately terminates the program). As far as I understand, according to the DIP, `noreturn` has a default constructor, but `noreturn` variables are initialized lazily when they are accessed.I think the best way to describe the behavior specified by the DIP is that the compiler rewrites noreturn x; to noreturn x = void; So, there is a special case here, but it does not require noreturn to have a default constructor.
Jan 17 2022
On Mon, Jan 17, 2022 at 05:57:19PM +0000, Paul Backus via Digitalmars-d wrote:On Monday, 17 January 2022 at 16:11:03 UTC, Timon Gehr wrote:The question then would be, why such a special case? There's no need to have a special case here, since aborting/terminating on declaring a variable of type noreturn is perfectly reasonable semantics for a bottom type. T -- What is Matter, what is Mind? Never Mind, it doesn't Matter.In D, types that do not have a default constructor cannot be default-constructed. The question is whether `noreturn`, the empty type, should really have a default constructor (that immediately terminates the program). As far as I understand, according to the DIP, `noreturn` has a default constructor, but `noreturn` variables are initialized lazily when they are accessed.I think the best way to describe the behavior specified by the DIP is that the compiler rewrites noreturn x; to noreturn x = void; So, there is a special case here, but it does not require noreturn to have a default constructor.
Jan 17 2022
On Monday, 17 January 2022 at 18:05:10 UTC, H. S. Teoh wrote:On Mon, Jan 17, 2022 at 05:57:19PM +0000, Paul Backus via Digitalmars-d wrote:Both the rewrite I describe and the behavior you propose are special cases. The non-special-case behavior is "compile-time error."I think the best way to describe the behavior specified by the DIP is that the compiler rewrites noreturn x; to noreturn x = void; So, there is a special case here, but it does not require noreturn to have a default constructor.The question then would be, why such a special case? There's no need to have a special case here, since aborting/terminating on declaring a variable of type noreturn is perfectly reasonable semantics for a bottom type.
Jan 17 2022
On Mon, Jan 17, 2022 at 06:11:09PM +0000, Paul Backus via Digitalmars-d wrote:On Monday, 17 January 2022 at 18:05:10 UTC, H. S. Teoh wrote:OK, so it should be a compile-time error, then. T -- Try to keep an open mind, but not so open your brain falls out. -- thebozOn Mon, Jan 17, 2022 at 05:57:19PM +0000, Paul Backus via Digitalmars-d wrote:Both the rewrite I describe and the behavior you propose are special cases. The non-special-case behavior is "compile-time error."I think the best way to describe the behavior specified by the DIP is that the compiler rewrites noreturn x; to noreturn x = void; So, there is a special case here, but it does not require noreturn to have a default constructor.The question then would be, why such a special case? There's no need to have a special case here, since aborting/terminating on declaring a variable of type noreturn is perfectly reasonable semantics for a bottom type.
Jan 17 2022
On Monday, 17 January 2022 at 18:40:55 UTC, H. S. Teoh wrote:On Mon, Jan 17, 2022 at 06:11:09PM +0000, Paul Backus via Digitalmars-d wrote:...and then you need special cases in generic code to deal with noreturn. So the question is, what's the cost/benefit of adding a "pragmatic" special case to the language here? In any case, the DIP has already gone through the full review process and been accepted, so whether we agree with it or not, the decision has already been made.Both the rewrite I describe and the behavior you propose are special cases. The non-special-case behavior is "compile-time error."OK, so it should be a compile-time error, then.
Jan 17 2022
On Mon, Jan 17, 2022 at 07:23:05PM +0000, Paul Backus via Digitalmars-d wrote:On Monday, 17 January 2022 at 18:40:55 UTC, H. S. Teoh wrote:What kind of special cases, specifically? Making variables of type noreturn illegal does not preclude taking parameters of type noreturn. Most current generic code does tests of the form: template someTpl(T) if (is(typeof((T t) { ... // tests on t here }))) { ... } This wouldn't be affected by the fact that T may be noreturn. If the generic function actually needs to instantiate a variable of type T, then it *should* be a compile error when T is noreturn anyway (because it can't possibly do anything meaningful with such a variable, and the fact that instantiation wasn't prevented means there's a logic error somewhere).On Mon, Jan 17, 2022 at 06:11:09PM +0000, Paul Backus via Digitalmars-d wrote:...and then you need special cases in generic code to deal with noreturn. So the question is, what's the cost/benefit of adding a "pragmatic" special case to the language here?Both the rewrite I describe and the behavior you propose are special cases. The non-special-case behavior is "compile-time error."OK, so it should be a compile-time error, then.In any case, the DIP has already gone through the full review process and been accepted, so whether we agree with it or not, the decision has already been made.Sine when does that mean things are set in stone? T -- Latin's a dead language, as dead as can be; it killed off all the Romans, and now it's killing me! -- Schoolboy
Jan 17 2022
On 17.01.22 20:39, H. S. Teoh wrote:What kind of special cases, specifically? Making variables of type noreturn illegal does not preclude taking parameters of type noreturn. Most current generic code does tests of the form: template someTpl(T) if (is(typeof((T t) { ... // tests on t here }))) { ... } This wouldn't be affected by the fact that T may be noreturn. If the generic function actually needs to instantiate a variable of type T, then it*should* be a compile error when T is noreturn anyway (because it can't possibly do anything meaningful with such a variable, and the fact that instantiation wasn't prevented means there's a logic error somewhere).+1. Exactly what I was wondering about too.
Jan 17 2022
On 17.01.22 20:39, H. S. Teoh wrote:Making variables of type noreturn illegalVariables of type `noreturn` should certainly remain legal, perhaps just not default initialization of such variables.does not preclude taking parameters of type noreturnSometimes it would, but it should not. E.g., this is fine: noreturn foo(noreturn x){ auto y=x; return y; }
Jan 17 2022
On Tue, Jan 18, 2022 at 12:55:06AM +0100, Timon Gehr via Digitalmars-d wrote:On 17.01.22 20:39, H. S. Teoh wrote:Hmm. This again treads that default-initialization / manual initialization fine line. I can see why such a thing would be useful (e.g., you pass a throwing (i.e., noreturn) predicate to a range function and expect that it should still compile and run), but having to special-case it seems to go against the entire point of having a noreturn type. Now I'm also lost as to what the "right" behaviour ought to be. :-D T -- They pretend to pay us, and we pretend to work. -- Russian sayingMaking variables of type noreturn illegalVariables of type `noreturn` should certainly remain legal, perhaps just not default initialization of such variables.does not preclude taking parameters of type noreturnSometimes it would, but it should not. E.g., this is fine: noreturn foo(noreturn x){ auto y=x; return y; }
Jan 18 2022
On 1/18/22 19:02, H. S. Teoh wrote:On Tue, Jan 18, 2022 at 12:55:06AM +0100, Timon Gehr via Digitalmars-d wrote:I am not creating any values, I am taking them from the caller. Execution already can't reach that variable.On 17.01.22 20:39, H. S. Teoh wrote:Hmm. This again treads that default-initialization / manual initialization fine line.Making variables of type noreturn illegalVariables of type `noreturn` should certainly remain legal, perhaps just not default initialization of such variables.does not preclude taking parameters of type noreturnSometimes it would, but it should not. E.g., this is fine: noreturn foo(noreturn x){ auto y=x; return y; }I can see why such a thing would be useful (e.g., you pass a throwing (i.e., noreturn) predicate to a range function and expect that it should still compile and run), but having to special-case it seems to go against the entire point of having a noreturn type. ...I don't understand where you see a special case here. Disallowing `noreturn` locals would be the special case.Now I'm also lost as to what the "right" behaviour ought to be. :-D TThere is no single "right" behaviour due to the prevalence of ad-hoc rules in D semantics. I guess if the compiler can show that the code is unreachable, then it can default initialize anything, but just allowing threading though the non-existent value itself is probably sufficient.
Jan 18 2022
On Monday, 17 January 2022 at 16:11:03 UTC, Timon Gehr wrote:On 16.01.22 23:56, Max Samukha wrote:Thanks, Timon! Don't construe the lack of a reply as disrespect. I'm slow, uneducated and still thinking.On Saturday, 15 January 2022 at 18:35:02 UTC, Paul Backus wrote:I share your confusion. A block of memory of size zero indeed has a unique value, not zero values. I don't think there is any principled way to arrive at a semantics where the program survives an attempt to initialize a `noreturn` variable. Also, note that typically, if a type `A*` is a subtype of a type `B*`, then `A.sizeof>=B.sizeof`. `noreturn*` is a subtype of any `T*`. Hence, `noreturn.sizeof` should be at least `size_t.max` or even `∞`. Thus, `noreturn.sizeof == 0` is already a special case with a pragmatic justification and using it to attempt to justify any further behavior of `noreturn` is questionable in the first place.Technically, a variable is a name associated with a block of memory, which may or may not contain a value. In the case of `noreturn`, the block of memory has size 0, and there are no possible values it can contain.Memory of size 0 contains a single value, which is the value of the unit type, 0-tuple, or whatever. Zero type is different. Timon, I am totally confused, could you clarify? ...In D, types that do not have a default constructor cannot be default-constructed. The question is whether `noreturn`, the empty type, should really have a default constructor (that immediately terminates the program). As far as I understand, according to the DIP, `noreturn` has a default constructor, but `noreturn` variables are initialized lazily when they are accessed. I am not sure whether that's pragmatically the right choice, because it's a special case and generic code might check whether some type is default-constructible and only in this case declare a local variable of that type. At least it crashes at the latest possible moment, I guess. It's certainly a bit weird that now, in D, 0·x is not always 0.So, technically, a `noreturn` variable is impossible to initialize. But since the compiler knows that the behavior of the program can't possibly depend on the variable's value, it is free to just skip the initialization altogether and leave the variable uninitialized instead.Sounds like "alias x = noreturn.init;"I guess you could make an argument that you should have to write `noreturn x = void;` every time you declare a `noreturn` variable. But even then, it would not be correct for `noreturn x;` to result in an `assert(0)` at runtime--it would have to be a *compile-time* error.I think it depends on the definition of local declaration. For statics, it is obviously a compile-time error. For locals, my feeling is it should be a runtime error, but who knows. Timon, help.
Feb 05 2022
On 1/14/22 12:52, WebFreak001 wrote:For example: ```d import std.stdio; void foo(noreturn a) { writeln("we got called!"); } void bar() { noreturn b; writeln("calling"); foo(b); } void main() { writeln("bar"); bar(); writeln("done"); } ``` Guess what it prints and reply to it on this thread. I think the result is pretty nonsensical. Reveal: https://run.dlang.io/is/4SXQal Should this usage be allowed? I would say not allowing it would make more sense.Debatable, but I think you have misidentified the questionable construct. I think the only question here is whether `noreturn` should have default construction. Maybe not. It should certainly be allowed as the type of a parameter or local variable, as avoiding that kind of special case is more or less the point of the feature.
Jan 14 2022
On 1/14/22 6:52 AM, WebFreak001 wrote:For example: ```d import std.stdio; void foo(noreturn a) { writeln("we got called!"); } void bar() { noreturn b; writeln("calling"); foo(b); } void main() { writeln("bar"); bar(); writeln("done"); } ``` Guess what it prints and reply to it on this thread. I think the result is pretty nonsensical. Reveal: https://run.dlang.io/is/4SXQal Should this usage be allowed? I would say not allowing it would make more sense. Additionally if we don't allow it we could allow aliasing to it like: ```d deprecated("OldType is no longer supported, use NewType instead") alias OldType = noreturn; ``` and with that both add hint messages for users to migrate and not allow broken code to compile. A sample for this would be libdparse, where the arguments of the visit functions determine which function is called, even if the argument isn't actually used or just ignored. The cases where the argument type is specified, but not used, do happen and migrating types there to force the user to rewrite code is currently not possible without completely removing the type.I have several thoughts here: 1. I can't find `noreturn` in the spec, so I'm not sure what exactly should happen here. In the library it has very very sparse documentation. This needs correction. At a minimum, it should go into [the Types page](https://dlang.org/spec/type.html). 2. The DIP clearly states that: Defining a variable with type noreturn with an initialization expression will simply generate that expression as soon as it becomes live. Defining a noreturn variable with no initialization expression generates an assert(0) only if the variable is accessed, which can be useful in generic code where unused noreturn variables may be declared. Generating the assert(0) as soon as the variable is live in these cases could prematurely end the program. So if the DIP is implemented as defined, the code in question is not assigning an initial value, and if it did, it should generate an assert. BUT, it's clearly being used, which means the call to `foo(b)` should be the equivalent of `foo(assert(0))`. 3. I wonder if there's some kind of dead-store optimization happening to thwart the error? -Steve
Jan 14 2022
On Friday, 14 January 2022 at 18:49:26 UTC, Steven Schveighoffer wrote:1. I can't find `noreturn` in the spec, so I'm not sure what exactly should happen here. In the library it has very very sparse documentation. This needs correction. At a minimum, it should go into [the Types page](https://dlang.org/spec/type.html).I made a PR to document noreturn with an example of a function that returns noreturn. I haven't found a good example for other behaviour yet. Some of the cases in the DIP don't work yet in stable dmd (explained in a comment): https://github.com/dlang/dlang.org/pull/3149 https://github.com/dlang/dlang.org/pull/3149#issuecomment-1001009778 I also made a PR for main() returning noreturn or inferred return.
Jan 14 2022