Introducing Nullable Reference Types in C#. Is there hope for D, too?

Timon Gehr timon.gehr at gmx.ch
Sun Nov 19 02:25:53 UTC 2017


On 19.11.2017 01:07, Walter Bright wrote:
> On 11/18/2017 6:16 AM, Timon Gehr wrote:
>> On 18.11.2017 05:05, Walter Bright wrote:
>>> On 11/17/2017 6:05 AM, Timon Gehr wrote:
>>>> There are type systems that do that, which is what is being proposed 
>>>> for C#. It's pretty straightforward: If I have a variable of class 
>>>> reference type C, it actually contains a reference to a class 
>>>> instance of type C.
>>>
>>> One of the difficulties with this is you'll still need an "empty" 
>>> instance of C for the non-null reference to point to.
>>
>> Why would you need an empty instance?
> 
> Consider the ClassDeclaration in the DMD source. Each ClassDeclaration 
> has a pointer to its base class, `baseClass`. Except for `Object`, which 
> doesn't have a base class. This is represented to assigning `null` to 
> `baseClass`.
> ...

I.e., baseClass should have type Nullable!ClassDeclaration. This does 
not in any form imply that ClassDeclaration itself needs to have a null 
value.

> So I can run up the base class list by:
> 
>      for (b = c; b; b = b.baseClass) { ... }
>  > If it cannot be null, I just have to invent something else that does the
> same thing:
> 
>      for (b = c; b != nullInstanceOfClass; b = b.baseClass) { ... }
> 
> and nothing really has changed.
> ...

Nullable!ClassDeclaration can be null, so this is not relevant.

>> Just use a Nullable!C instead of a C if a special 'null' state is 
>> actually required.
> 
> What should the default initializer for a type do?
> ...

There should be none for non-nullable types.

> 
>>> Any attempts to use a method on the empty instance should throw. 
>>
>> The idea is that the type system makes potential such attempts 
>> explicit while verifying that they don't occur in most of the cases. 
>> Then you can grep for potential null dereferences.
> 
> There are cases where the actual path taken guarantees initialization, 
> but the graph of all paths does have uninitialized edges. Figuring out 
> which are paths never taken is the halting problem.

The same applies to all other errors prevented by a type system. It's 
just not a useful argument. The halting problem is undecidable mostly 
because it is possible to write ridiculous programs. The ones we write 
in practice are often easier to understand (especially when they come 
with some useful documentation), because they were /designed/ to serve a 
particular purpose. Note that the undecidability of the halting problem 
is not something that applies exclusively to programs, it also applies 
to programmers.

> I found this out 
> when testing my DFA (data flow analysis) algorithms.
> 
>    void test(int i) {
>      int* p = null;
>      if (i) p = &i;
>      ...
>      if (i) *p = 3;
>      ...
>    }
> 
> Note that the code is correct, but DFA says the *p could be a null 
> dereference. (Replace (i) with any complex condition that there's no way 
> the DFA can prove always produces the same result the second time.)

Yes, there is a way. Put in an assertion. Of course, at that point you 
are giving up, but this is not the common case. Also, you can often just 
write the code in a way that the DFA will understand. We are doing this 
all the time in statically-typed programming languages.


More information about the Digitalmars-d mailing list