January 17, 2019
Am 17.01.19 um 15:21 schrieb Jonathan Marler:
> The DIP says this:
> 
>     Any attempt to use the value of a Tbottom expression is an error.
> 
> What does this mean? Just before this statement, we see a set of examples of how TBottom can be "used" inside expressions, i.e.
> 
>     a || b
> 
> So what does that statement mean then?
> 

I asked the same question earlier. What I _think_ it means is that you
can never assign it to a variable, instanciate a variable with it or
pass it to functions (maybe I forgot a few things). The main idea is
that you can not use it in such a way, that you would actually need a value.
January 17, 2019
Am 17.01.19 um 21:15 schrieb H. S. Teoh:
> On Thu, Jan 17, 2019 at 06:51:13PM +0000, Jonathan Marler via Digitalmars-d wrote: [...]
>> To summarize, the bottom type allows you to declare a special pointer with no storage!
>>
>>     TBottom*.sizeof == 0
>>
>> This is also something that should be in the DIP and Walter should chime in with whether or not he thinks these semantics can actually be implemented.
> 
> This would introduce an asymmetry with the rest of the pointer types in the language which all have equal sizes, and this asymmetry will percolate down to other language constructs, causing special cases and inconsistencies everywhere.  I don't recommend doing this.
> 
> A better approach might be to make TBottom* always equal to null -- i.e., it's always illegal to dereference it because no instances of TBottom can exist.
> 
> (Of course, TBottom.sizeof would have to be either 0 or some kind of non-existent value, because instances of TBottom cannot actually exist. Based on the principle of symmetry, I'd think it should be 0, because making it an error would imply specialcasing generic code that may unknowingly be handed TBottom as a template type argument, and making it return some other value like -1 or size_t.min may cause problems in other generic code that might, for example, wish to allocate storage of the given size.  OTOH, maybe a negative .sizeof might be just the ticket to prevent generic code from trying to instantiate TBottom.  Either way, it would have to be treated specially -- which also means there's a price to be paid for adding TBottom to the language: there is no free lunch.)
> 
> 
> T
> 
+1 for making Tbottom* have the single value `null` (this totally makes sense, it is a type which holds `null` or the address of a value of type `Tbottom`, but no such address can exist, so it can only hold `null`).

`sizeof(Tbottom*) == 0` is then a logical consequence, even if it is inconsistent with the size of other pointer types.
January 17, 2019
On Thu, 17 Jan 2019 21:48:52 +0100, Johannes Loher wrote:
> I don't actually think declaring a variable of the Bottom type is required to be illegal. It just needs to be impossible for it to ever be initialized.

Which runs into default-initialization, so that's not awesome. But the same is true of structs with `@disable this();`

If we treated them the same, you'd still be able to write:

    TBottom var = void;
    doStuff(var);

That's...not awesome.
January 17, 2019
Am 17.01.19 um 22:29 schrieb Neia Neutuladh:
> On Thu, 17 Jan 2019 21:48:52 +0100, Johannes Loher wrote:
>> I don't actually think declaring a variable of the Bottom type is required to be illegal. It just needs to be impossible for it to ever be initialized.
> 
> Which runs into default-initialization, so that's not awesome. But the same is true of structs with `@disable this();`
> 
> If we treated them the same, you'd still be able to write:
> 
>     TBottom var = void;
>     doStuff(var);
> 
> That's...not awesome.
> 
Both default initialization and void initialization of variables of type Tbottom would need to be illegal. The _only_ way to "initialize" them would be by copying, which—as explained—means they are never actually initialized.
January 17, 2019
On Thursday, 17 January 2019 at 20:15:20 UTC, H. S. Teoh wrote:
> On Thu, Jan 17, 2019 at 06:51:13PM +0000, Jonathan Marler via Digitalmars-d wrote: [...]
>> To summarize, the bottom type allows you to declare a special pointer with no storage!
>> 
>>     TBottom*.sizeof == 0
>> 
>> This is also something that should be in the DIP and Walter should chime in with whether or not he thinks these semantics can actually be implemented.
>
> This would introduce an asymmetry with the rest of the pointer types in the language which all have equal sizes, and this asymmetry will percolate down to other language constructs, causing special cases and inconsistencies everywhere.  I don't recommend doing this.

Yes that's kind of the point :)  It's a new pointer type that has a unique size from all other pointer types.  This means you can no-longer assume all pointers types are the same size, you have a special case where it's size can be zero. This might warrant some code changes to handle this case, but does enable some new semantics which can be useful for library writers.  Case in point was you can now have a template parameter T* that the user can instantiate in such a way as to eliminate the pointer alltogether.

>
> A better approach might be to make TBottom* always equal to null -- i.e., it's always illegal to dereference it because no instances of TBottom can exist.
>

That's conceptually the same thing.  Saying that TBottom* is "always equal to null" is the same as saying it's a Unit Type, which is the same as saying that it contains no information so the storage needed is 0 (same as void).

January 17, 2019
Am 17.01.19 um 22:29 schrieb Neia Neutuladh:
> On Thu, 17 Jan 2019 21:48:52 +0100, Johannes Loher wrote:
>> I don't actually think declaring a variable of the Bottom type is required to be illegal. It just needs to be impossible for it to ever be initialized.
> 
> Which runs into default-initialization, so that's not awesome. But the same is true of structs with `@disable this();`
> 
> If we treated them the same, you'd still be able to write:
> 
>     TBottom var = void;
>     doStuff(var);
> 
> That's...not awesome.
> 
By the way, I think it is debatable whether variables of type Tbottom should be declarable or not. The inconsistencies (in particular no void initialization which every other type has) are indeed a reason to not allow this. But then again, this is also an inconsistency: All other types are declarable, except for void (which should also be fixed in my opinion).

There are quite a few languages which allow declaration of variables of
their bottom type in the manner I described. These include:
- Rust
- Kotlin
- Flow
- Haskell
- Swift

On the other hand, I don't know of any language which prevents this. Do you know any?

January 17, 2019
On Thu, 17 Jan 2019 12:15:20 -0800, H. S. Teoh wrote:
> A better approach might be to make TBottom* always equal to null -- i.e., it's always illegal to dereference it because no instances of TBottom can exist.

Dereferencing TBottom* would have to be rewritten as `assert(false);` to make this type of illegal work with generic code.
January 17, 2019
Am 17.01.19 um 22:59 schrieb Neia Neutuladh:
> On Thu, 17 Jan 2019 12:15:20 -0800, H. S. Teoh wrote:
>> A better approach might be to make TBottom* always equal to null -- i.e., it's always illegal to dereference it because no instances of TBottom can exist.
> 
> Dereferencing TBottom* would have to be rewritten as `assert(false);` to make this type of illegal work with generic code.
> 
No, dereferencing an instance of type `Tbottom*` is always dereferencing
`null`, i.e. programm abbortion (which is not the same as `assert(0)`).
January 17, 2019
On Thu, Jan 17, 2019 at 09:48:52PM +0100, Johannes Loher via Digitalmars-d wrote: [...]
> I don't actually think declaring a variable of the Bottom type is required to be illegal. It just needs to be impossible for it to ever be initialized. This can trivially be done by only allwoing it to be initialized by copying because whenever you have an expression of type Bottom to which you "initialize" a variable of type Bottom, that expression never returns and thus the initialization never actually takes place.

That's an interesting take on Bottom. I think that makes sense.  In D, we have default initialization, so a declaration like:

	Bottom b;

implicitly means (in pseudocode):

	Bottom b = void; // (yikes, another meaning of `void`)
	b = b.init;

Since b.init does not exist, this would result in a compile error.

However, if you initialized it with an expression of type Bottom, then it would "work":

	Bottom func() {...}
	Bottom b = func();

This is OK, because func() never returns (by definition), so the assignment to b never takes place.  Which also means the compiler can simply elide b from any stack-allocation code and skip emitting code for the assignment, etc., because it is unreachable code.  All subsequent code would also be unreachable and can be elided.


> This would still allow things like
> ```
> Bottom var = assert(0);
> ```

Yes.  Though *why* you'd want to declare a variable of type Bottom -- as opposed to just writing the expression of type Bottom -- is a good question that I don't have a good answer to.


> and also
> ```
> void fun(Bottom b)
> {
>     // whatever, this function can never actually be called
> }
[...]

This is an interesting one.  IOW, the parameter Bottom b is basically equivalent to "this function is never reached".  So it can be completely elided from codegen?  This would let you write strange things like:

	fun(assert(0));

though it does make one wonder why write `fun` at all instead of just
`assert(0)` directly.

But on second thoughts... something like this could be extremely useful in generic code.  For example, you could have a generic function that invokes a user callback and returns the value returned by the callback. You could then pass in a function that returns Bottom (i.e., it never terminates), then the generic function will automatically be inferred to be of type Bottom, and the compiler could stop codegen as soon as it encounters the first expression of type Bottom -- we get non-termination analysis for free, as a natural consequence of the type system!

In fact, declaring a variable of type Bottom then becomes a synonym for writing assert(0).  Generic code can then freely operate on any input type T, and if T == Bottom, then the type system automatically performs reachability analysis for us, and the codegen will only need to go as far as the first occurrence of Bottom.  It can then just emit hlt and exit codegen for that function.  It wouldn't matter what expression Bottom was found in: it could be nested somewhere inside a complex expression, and the type system automatically figures out that the type is Bottom and therefore codegen can just stop emitting code at that point.  There would be no need to special-case checking for assert(0) to elide unreachable code, and no need for the template code to use static if to check for Bottom.  It just falls out of the type system naturally.

Even better yet, this means we don't even need to make it an error to declare and initialize a variable of type Bottom: the initialization can just be defined to be `assert(0);`, i.e.:

	Bottom b;

becomes equivalent to (in pseudo-code):

	Bottom b = void;
	b = assert(0);

which simplifies to just:

	assert(0);

The unreachability then just naturally falls out of the type system. The compiler can compile the function as normal, as though we were actually initializing a variable of type Bottom, then during codegen the optimizer sees the `assert(0);` and elides everything else that follows it.  At runtime, this would just abort at the point where b is initialized.

This also lets us write sanity-checking expressions such as:

	float sqrt(float x) {
		return (x >= 0.0) ? ... /* implementation here */
			: Bottom.init;
	}

This is valid since Bottom converts to any type. Then at runtime, if x <
0.0, assert(0) gets triggered.  There would be no need to write an
if-statement that explicitly calls assert(0).

Nice, well-rounded-out semantics.


T

-- 
Those who don't understand Unix are condemned to reinvent it, poorly.
January 17, 2019
On Thu, Jan 17, 2019 at 09:29:53PM +0000, Neia Neutuladh via Digitalmars-d wrote:
> On Thu, 17 Jan 2019 21:48:52 +0100, Johannes Loher wrote:
> > I don't actually think declaring a variable of the Bottom type is required to be illegal. It just needs to be impossible for it to ever be initialized.
> 
> Which runs into default-initialization, so that's not awesome. But the same is true of structs with `@disable this();`
> 
> If we treated them the same, you'd still be able to write:
> 
>     TBottom var = void;
>     doStuff(var);
> 
> That's...not awesome.

It looks that way, but actually it totally makes sense.  It only looks insane because we're thinking of it as a standalone, explicit function. But it's a lot less insane if you think of generic code that take arbitrary type parameters, e.g.:

	void func(T)(bool choice)
	{
		if (choice)
		{
			T var;
			doStuff(var);
		}
	}

In this case, it's an asymmetry if func doesn't compile when T == Bottom.  The writer of func would have to use sig constraints or static if to specifically check for Bottom.

If instead we allow the declaration of `Bottom var;`, with the stipulation that it corresponds with the runtime code for halting the program (e.g., assert(0)), then the implementor of func wouldn't even have to think about the possibility of T == Bottom.  When the user passes Bottom to func, the function naturally just aborts where var is declared.

This then allows func to be used both for invoking doStuff if `choice` is true, and also, by passing T == Bottom, to *abort* the program if `choice` is true.  This becomes fully transparent to the implementor of `func` -- you wouldn't need to special-case Bottom, and you wouldn't need to write another function specifically made to abort the program when choice == true.  The same code would work for all cases.


T

-- 
Why can't you just be a nonconformist like everyone else? -- YHL