www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - bottom type as parameter or local variable, does that make sense?

reply WebFreak001 <d.forum webfreak.org> writes:
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
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
parent reply vit <vit vit.vit> writes:
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??! T
Whith explicit init it works like that: ```d import std.stdio; void main(){ noreturn n = noreturn.init; writeln("bar"); } ``` Print: ``` //Illegal instruction (core dumped) ```
Jan 14 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
next sibling parent reply bauss <jj_1337 live.dk> writes:
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:
 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
Why is it even allowed, that's beyond my understanding?
Jan 14 2022
parent Nick Treleaven <nick geany.org> writes:
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
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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.
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)`.
Jan 14 2022
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
[...]
 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.
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.
 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
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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`.
? 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.
Jan 14 2022
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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:
 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`.
? 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.
noreturn.init is assert(0), which compiles.
 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
 
My 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
parent reply Tejas <notrealemail gmail.com> writes:
On Friday, 14 January 2022 at 20:59:18 UTC, Timon Gehr wrote:
 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:
 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`.
? 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.
noreturn.init is assert(0), which compiles.
 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
 
My 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.
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 undesirable
Jan 15 2022
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 15.01.22 12:02, 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.
 ...
Exactly. Unless.
 `noreturn` not default initializing is therefore a special case, which I 
 feel is undesirable
Maybe, 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
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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 undesirable
But 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
prev sibling parent reply Max Samukha <maxsamukha gmail.com> writes:
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
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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.
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`*.
Jan 14 2022
parent reply Max Samukha <maxsamukha gmail.com> writes:
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
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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.
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.
Jan 15 2022
parent reply Max Samukha <maxsamukha gmail.com> writes:
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
parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 15 January 2022 at 16:34:55 UTC, Max Samukha wrote:
 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.
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.
Jan 15 2022
parent reply Max Samukha <maxsamukha gmail.com> writes:
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
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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.
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.
Jan 15 2022
parent reply Max Samukha <maxsamukha gmail.com> writes:
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
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
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:
 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?
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.
 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;"
You can't `alias` an expression, so the above is ill-formed.
 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.
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.
Jan 16 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
[...]
 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.
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 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
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 16.01.22 23:56, Max Samukha wrote:
 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? ...
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.
 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.
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.
Jan 17 2022
next sibling parent reply Elronnd <elronnd elronnd.net> writes:
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
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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?
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.
Jan 17 2022
parent reply Elronnd <elronnd elronnd.net> writes:
On Monday, 17 January 2022 at 18:05:49 UTC, H. S. Teoh wrote:
 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.
You can't do pointer arithmetic on class instances.
Jan 17 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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.
You can't do pointer arithmetic on class instances.
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 Champion
Jan 17 2022
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 1/17/22 18:53, Elronnd wrote:
 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.
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.
 
 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
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
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
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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.
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.
Jan 17 2022
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 
 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.
Both the rewrite I describe and the behavior you propose are special cases. The non-special-case behavior is "compile-time error."
Jan 17 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 On Mon, Jan 17, 2022 at 05:57:19PM +0000, Paul Backus via Digitalmars-d
 wrote:
 
 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.
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. T -- Try to keep an open mind, but not so open your brain falls out. -- theboz
Jan 17 2022
parent reply Paul Backus <snarwin gmail.com> writes:
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:
 
 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.
...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.
Jan 17 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 On Mon, Jan 17, 2022 at 06:11:09PM +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."
OK, so it should be a compile-time error, then.
...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?
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).
 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
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 17.01.22 20:39, H. S. Teoh wrote:
 Making variables of type noreturn illegal
Variables of type `noreturn` should certainly remain legal, perhaps just not default initialization of such variables.
 does not preclude taking parameters of type noreturn
Sometimes it would, but it should not. E.g., this is fine: noreturn foo(noreturn x){ auto y=x; return y; }
Jan 17 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 Making variables of type noreturn illegal
Variables of type `noreturn` should certainly remain legal, perhaps just not default initialization of such variables.
 does not preclude taking parameters of type noreturn
Sometimes it would, but it should not. E.g., this is fine: noreturn foo(noreturn x){ auto y=x; return y; }
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 saying
Jan 18 2022
parent Timon Gehr <timon.gehr gmx.ch> writes:
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:
 On 17.01.22 20:39, H. S. Teoh wrote:
 Making variables of type noreturn illegal
Variables of type `noreturn` should certainly remain legal, perhaps just not default initialization of such variables.
 does not preclude taking parameters of type noreturn
Sometimes it would, but it should not. E.g., this is fine: noreturn foo(noreturn x){ auto y=x; return y; }
Hmm. This again treads that default-initialization / manual initialization fine line.
I am not creating any values, I am taking them from the caller. Execution already can't reach that variable.
 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
 
 
 T
 
There 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
prev sibling parent Max Samukha <maxsamukha gmail.com> writes:
On Monday, 17 January 2022 at 16:11:03 UTC, Timon Gehr wrote:
 On 16.01.22 23:56, Max Samukha wrote:
 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? ...
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.
 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.
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.
Thanks, Timon! Don't construe the lack of a reply as disrespect. I'm slow, uneducated and still thinking.
Feb 05 2022
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
parent Nick Treleaven <nick geany.org> writes:
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