Null-checked reference types

Richard (Rikki) Andrew Cattermole richard at cattermole.co.nz
Wed Aug 7 11:30:02 UTC 2024


On 07/08/2024 11:22 PM, Quirin Schroll wrote:
> On Wednesday, 7 August 2024 at 01:39:29 UTC, Richard (Rikki) Andrew 
> Cattermole wrote:
>> This allows you to do both loads and stores and do something if it 
>> failed transitively.
>>
>> ```d
>> if (var1.var2?.var3?.field = 3) {
>>     // success
>> } else {
>>     // failure
>> }
>> ```
> 
> I somehow don’t like `if (… = …)` when it’s not a declaration. At first 
> sight, I thought you intended `… == 3`.

It's going to be valid regardless, due to AssignExpression.

>> > No data flow analysis is proposed. Null checking is local and
>> done by tracking ? and ! by the type system.
>>
>> DFA is only required if you want the type state to change as the 
>> function is interpreted. So that's fine. That is a me thing to figure 
>> out.
> 
> If I understand correctly, by “type state” you means something like 
> value range propagation. It basically *is* value range propagation, 
> however the ranges in question are `null` and all non-null values. You 
> don’t suggest `typeof` type of a variable or expression changes, 
> correct? (I think that would be very weird.)

No, I meant type state.

https://en.wikipedia.org/wiki/Typestate_analysis

unreachable < reachable < initialized < default-initialized < non-null < 
user

>> However, you do not need to annotate function body variables with this 
>> approach.
>>
>> Look at the initializer of a function variable declaration, it'll tell 
>> you if it has the non-null type state.
>>
>> ```d
>> int* ptr1;
>> int* ptr2 = ptr1;
>> ```
> 
> The only issue is, just because e.g. a pointer is initialized with 
> something non-null (e.g. the address of a variable), that doesn’t mean 
> some logic later won’t assign `null` to it.

Right, that would have to be disallowed without DFA, since the type 
state must not change throughout a function body.

>> However the problem which caused me some problems in the past is on 
>> tracking variables outside of a function. You cannot do it.
>>
>> Variables outside a function change type state during their lifespan. 
>> They have the full life cycle, starting at reachable, into non-null 
>> and then back to reachable. If you tried to force it to be non-null, 
>> the language would force you to have an .init value that is non-null. 
>> This is an known issue with classes already. It WILL produce logic 
>> errors that are undetectable.
> 
> I don’t care much about tracking. Probably, with `if (auto) ...`, you 
> can just rename the variable, but typed non-nullable:
> 
> ```d
> void f(int*? p)
> {
>      if (int* q = p) ... else return;
>      int v = *q; // no error, q isn’t nullable, not by analysis, just by 
> type
> }
> ```

What matters here is that you do not need to add annotation to the type 
itself. It only needs to exist within the function signature. Anywhere 
else its useless information.


More information about the dip.development mailing list