Null references redux

downs default_357-line at yahoo.de
Sun Sep 27 13:13:48 PDT 2009


Jeremie Pelletier wrote:
> downs wrote:
>> Jeremie Pelletier wrote:
>>> Christopher Wright wrote:
>>>> Jeremie Pelletier wrote:
>>>>> What if using 'Object obj;' raises a warning "unitialized variable"
>>>>> and makes everyone wanting non-null references happy, and 'Object obj
>>>>> = null;' raises no warning and makes everyone wanting to keep the
>>>>> current system (all two of us!) happy.
>>>>>
>>>>> I believe it's a fair compromise.
>>>> It's a large improvement, but only for local variables. If your
>>>> segfault has to do with a local variable, unless your function is
>>>> monstrously large, it should be easy to fix, without changing the type
>>>> system.
>>>>
>>>> The larger use case is when you have an aggregate member that cannot
>>>> be null. This can be solved via contracts, but they are tedious to
>>>> write and ubiquitous.
>>> But how would you enforce a nonnull type over an aggregate in the first
>>> place? If you can, you could also apply the same initializer semantics I
>>> suggested earlier.
>>>
>>> Look at this for example:
>>>
>>> struct A {
>>>     Object cannotBeNull;
>>> }
>>>
>>> void main() {
>>>     A* a = new A;
>>> }
>>>
>>> Memory gets initialized to zero, and you have a broken non-null type.
>>> You could have the compiler throw an error here, but the compiler cannot
>>> possibly know about all data creation methods such as malloc, calloc or
>>> any other external allocator.
>>>
>>> You could even do something like:
>>>
>>> Object* foo = calloc(Object.sizeof);
>>>
>>> and the compiler would let you dereference foo resulting in yet another
>>> broken nonnull variable.
>>>
>>> Non-nulls are a cute idea when you have a type system that is much
>>> stricter than D's, but there are just way too many workarounds to make
>>> it crash in D.
>>
>> "Here are some cases you haven't mentioned yet. This proves that the
>> compiler can't possibly be smart enough. "
>>
>> Yeeeeeah.
> 
> I allocate most structs on the gc, unless I need them only for the scope
> of a function (that includes RVO). All objects are on the gc already, so
> it's a pretty major case. The argument was to protect aggregate fields,
> I'm just pointing out that their usage usually is preventing an easy
> implementation. I'm not saying its impossible.
> 
> Besides, what I said was, if its possible to enforce these fields to be
> null/non-null, you can enforce them to be properly initialized in such
> case, making nulls/non-nulls nearly useless.
> 
>> In the above case, why not implicitly put the cannotBeNull check into
>> the struct invariant? That's where it belongs, imho.
> 
> Exactly, what's the need for null/non-null types then?
> 

You're twisting my words.

Checking for null in the struct invariant would be an _implementation_ of non-nullable types in structs.

Isn't the whole point of defaulting to non-nullable types that we don't have to check for it manually, i.e. in the user-defined invariant?

I think we should avoid having to build recursive checks for null-ness for every type we define.

>> Regarding your example, it's calloc(size_t.sizeof). And a) we probably
>> can't catch that case except with in/out null checks on every method,
>> but then again, how often have you done that? I don't think it's
>> relevant enough to be relevant to this thread. :)
> 
> Actually, sizeof currently returns the size of the reference, so its
> always going to be the same as size_t.sizeof.

Weird. I remembered that differently. Thanks.



More information about the Digitalmars-d mailing list