July 18, 2017
On Tuesday, 18 July 2017 at 21:35:21 UTC, Moritz Maxeiner wrote:
> Could you explain why `return foo();` is even legal for a `void foo() {}`?

Suppose you are writing a template function that forwards:

auto forward(alias fun, T...)(T args) {
   return fun(args);
}


It just saves you from having to static if(fun returns void).
July 18, 2017
On 7/17/2017 4:26 PM, H. S. Teoh via Digitalmars-d wrote:
> But the point is that so much time and effort is being spent on
> discussing and designing a feature that you have admitted yourself to be
> "rarely used". As a disinterested bystander I find it somewhat amusing
> (and sad) to see so much over-engineering of an overly-complex system
> involving a new basic type in the language, which in turn entails all
> sorts of corner cases in how it will interact with existing types and
> constructs, not to mention the implementation complexities that will be
> involved to pull it off -- all for what?  Just to be able to say
> "function F doesn't return".  Seems like disproportionate effort for
> only marginal returns (har har).

The issue here (for me) is to have a type system that matches type systems used in type theory. D already has strong abilities to do type calculus. Instead of inventing our own whackadoodle scheme, one hack at a time, why not match existing type calculus? Then, attempts to do type calculus in D will work as worked out by type theory.

Or, we could go with the C++ approach which historically is to add an ad-hoc solution for an existing problem, and then another ad-hoc solution for the whacky effects that turned out to have, rinse, repeat. (Look at all the different ways to do type deduction, a horrifying consequence. Or function overloading, which started with complex special cases, then changed to partial ordering for special cases.)

There is just something fundamentally wrong with:

    @noreturn int foo();

returning a value yet not returning. It smacks of "the language designer(s) are idiots." It winds up complicating things like:

    auto x = a ? b : foo();

What is the type of x? @noreturn means a special case. A proper bottom type means it is not a special case.


I recall that Rust initially did @noreturn as a special case, and later replaced that with a bottom type and integrated it into the type system. I understand that this had a positive ripple effect, such as reducing special cases in user generic code.

Noreturn functions are just a happy fallout of doing this correctly in the first place. The language semantics and compiler internals should be simpler and cleaner by using accepted type theory.
July 18, 2017
On Tuesday, 18 July 2017 at 21:45:27 UTC, Adam D. Ruppe wrote:
> On Tuesday, 18 July 2017 at 21:35:21 UTC, Moritz Maxeiner wrote:
>> Could you explain why `return foo();` is even legal for a `void foo() {}`?
>
> Suppose you are writing a template function that forwards:
>
> auto forward(alias fun, T...)(T args) {
>    return fun(args);
> }
>
>
> It just saves you from having to static if(fun returns void).

That's a good pragmatic (syntactic sugar) reason, thanks.
July 19, 2017
On 18.07.2017 23:35, Moritz Maxeiner wrote:
> 
> Could you explain why `return foo();` is even legal for a `void foo() {}`?

Because the ad-hoc decision to make void a type that is not really a type leads to unnecessary friction, and this exceptional rule removes the friction in one common special case.

> I wasn't aware of it before and the fact that you can (syntactically) return the non-existent return value of `foo` raises cognitive dissonance flags for me. I imagine there's a type system reason?

There should be. foo's return type could be a unit type, with just one value. Then foo does have a return value, but it is always the same and so does not need to be explicitly tracked.
July 18, 2017
On Tuesday, 18 July 2017 at 22:03:27 UTC, Walter Bright wrote:
> On 7/17/2017 4:26 PM, H. S. Teoh via Digitalmars-d wrote:
>> But the point is that so much time and effort is being spent on
>> discussing and designing a feature that you have admitted yourself to be
>> "rarely used". As a disinterested bystander I find it somewhat amusing
>> (and sad) to see so much over-engineering of an overly-complex system
>> involving a new basic type in the language, which in turn entails all
>> sorts of corner cases in how it will interact with existing types and
>> constructs, not to mention the implementation complexities that will be
>> involved to pull it off -- all for what?  Just to be able to say
>> "function F doesn't return".  Seems like disproportionate effort for
>> only marginal returns (har har).
>
> The issue here (for me) is to have a type system that matches type systems used in type theory. D already has strong abilities to do type calculus. Instead of inventing our own whackadoodle scheme, one hack at a time, why not match existing type calculus? Then, attempts to do type calculus in D will work as worked out by type theory.
>
> Or, we could go with the C++ approach which historically is to add an ad-hoc solution for an existing problem, and then another ad-hoc solution for the whacky effects that turned out to have, rinse, repeat. (Look at all the different ways to do type deduction, a horrifying consequence. Or function overloading, which started with complex special cases, then changed to partial ordering for special cases.)

> [...]

Agreed. Discovered vs invented as Philip Wadler classifies the two approaches in his talk: https://www.youtube.com/watch?v=IOiZatlZtGU, which I highly recommend watching.
July 19, 2017
On Tuesday, 18 July 2017 at 22:03:27 UTC, Walter Bright wrote:
> The issue here (for me) is to have a type system that matches type systems used in type theory. D already has strong abilities to do type calculus. Instead of inventing our own whackadoodle scheme, one hack at a time, why not match existing type calculus? Then, attempts to do type calculus in D will work as worked out by type theory.
>
> Or, we could go with the C++ approach which historically is to add an ad-hoc solution for an existing problem, and then another ad-hoc solution for the whacky effects that turned out to have, rinse, repeat. (Look at all the different ways to do type deduction, a horrifying consequence. Or function overloading, which started with complex special cases, then changed to partial ordering for special cases.)
>
> There is just something fundamentally wrong with:
>
>     @noreturn int foo();

I would understand it to mean that if it were to return, foo would return an int but it is undefined behaviour for foo to dynamically return.

> returning a value yet not returning. It smacks of "the language designer(s) are idiots." It winds up complicating things like:
>
>     auto x = a ? b : foo();
>
> What is the type of x? @noreturn means a special case. A proper bottom type means it is not a special case.

int. @noreturn need not pollute the type, given the use cases for @noreturn. Namely to document that the function does not dynamically return and aid the compiler in optimisation (are there any other uses?). `assert(0);` is already accepted in the front end as an acceptable return "value" for any type e.g. in

Bar foo(int x)
{
    foreach (e; data[])
         if (e.x == x)
              return e;
    assert(0);
}

> The language semantics and compiler internals should be simpler and cleaner by using accepted type theory.

Not for LDC or GDC. They already have the ability to signal to their backends that a function does not dynamically return.

as I have posted before, one can do (in core.attribute),

enum __noreturn;

version(LDC)
{
    import ldc.attributes : llvmAttr;
    alias noreturn = AliasSeq!(llvmAttr("noreturn"),__noreturn);
}
else version(GNU)
{
    import gcc.attribute : llvmAttr;
    alias noreturn = AliasSeq!(attribute("noreturn"),__noreturn);
}
else // DMD
{
    alias noreturn = __noreturn;
}

for a complete implementation for LDC and GDC, and DMD can do whatever it needs to with the presence of __noreturn, including fronted semantic analysis.

July 18, 2017
On 7/18/2017 5:54 PM, Nicholas Wilson wrote:
>> There is just something fundamentally wrong with:
>>     @noreturn int foo();
> I would understand it to mean that if it were to return, foo would return an int but it is undefined behaviour for foo to dynamically return.

That's the C++ behavior. I know we are all accustomed to it and hence think it is intuitive, but it isn't. I know I've had a hard time breaking free of this sort of thinking, having been so deeply immersed in C++ for so long.


>> returning a value yet not returning. It smacks of "the language designer(s) are idiots." It winds up complicating things like:
>>
>>     auto x = a ? b : foo();
>>
>> What is the type of x? @noreturn means a special case. A proper bottom type means it is not a special case.
> 
> int.

And if b is of type `T`? It doesn't make sense to have to give a type to something that does not return. (@noreturn functions are usually typed as returning `void` anyway, but that still doesn't make much sense.)

I know how noreturn attributes work - I implemented them decades ago in DMC and DMC++. They are supported by the DMD optimizer and back end.

But they are a hack to the type system, and I suggest an unnecessary one.

The backends for DMD, LDC and GDC would not be affected at all by the addition of a bottom type to the front end, and it would be trivial for the glue code to add the noreturn attribute for functions that return the bottom type.
July 19, 2017
On Wednesday, 19 July 2017 at 01:52:30 UTC, Walter Bright wrote:
> I know how noreturn attributes work - I implemented them decades ago in DMC and DMC++. They are supported by the DMD optimizer and back end.
>
> But they are a hack to the type system, and I suggest an unnecessary one.

It describe the behaviour of the function: I think it is neither necessary nor a good idea to express it as a type. assert(0) is already accepted as a valid return statement of any type.

> The backends for DMD, LDC and GDC would not be affected at all by the addition of a bottom type to the front end, and it would be trivial for the glue code to add the noreturn attribute for functions that return the bottom type.

Said glue would be unnecessary with an attribute *that already exists*.
July 18, 2017
On 7/18/2017 7:14 PM, Nicholas Wilson wrote:
> It describe the behaviour of the function: I think it is neither necessary nor a good idea to express it as a type. assert(0) is already accepted as a valid return statement of any type.

I can't continue this without being repetitive, so we'll just have to disagree.


>> The backends for DMD, LDC and GDC would not be affected at all by the addition of a bottom type to the front end, and it would be trivial for the glue code to add the noreturn attribute for functions that return the bottom type.
> 
> Said glue would be unnecessary with an attribute *that already exists*.

In the glue code, replace:

    if (function attribute is 'noreturn')
	set backend attribute to 'noreturn';

with:

    if (function return type is 'bottom')
	set backend attribute to 'noreturn';
	set backend function return type to 'void';

and it should be ready to rock :-)
July 19, 2017
On Sunday, 16 July 2017 at 20:44:13 UTC, Andrei Alexandrescu wrote:
> Perhaps we go the inverse route and define the bottom type as typeof(*null). Would that simplify matters? There is some good consistency about it:
>
> null: a pointer to anything. But can't be dereferenced.
> *null: well, therefore... anything. But can't be created.

That sounds more like a top type, though, because as you said it can be "anything". A bottom type can not be anything, but only nothing.