March 04, 2009
Walter Bright escribió:
> Ary Borenszweig wrote:
>> It's not like that. They don't require you to initialize a variable in it's initializer, but just before you read it for the fist time. That's very different.
> 
> The only way to do that 100% reliably is to instrument the running code.

Java does it on compile time.

Ah, I forgot to say, this is only done for local variables. For member variables the default initializer is used.

If it's done only for local variables then you don't need to instrument the running code.
March 04, 2009
bearophile wrote:
> Walter Bright:
>> Joel C. Salomon:
>>> To avoid this class of bug, you need a simple way to declare what
>>> the acceptable values for a variable are.
>> Languages have had this capability, but it never caught on. People
>> found it just too tedious.
> 
> What? We use ranges of integers in Delphi at work today :-) I have
> even proposed something similar for D twice in the past. (But to be
> precise, I often don't use ranged integral numbers for the purpose
> discussed here).

I didn't mean nobody liked them or used them. I mean they have not caught on, despite having been around for 3 decades at least.
March 04, 2009
Andrei Alexandrescu wrote:
> Walter Bright wrote:
>> Ary Borenszweig wrote:
>>> It's not like that. They don't require you to initialize a variable in it's initializer, but just before you read it for the fist time. That's very different.
>>
>> The only way to do that 100% reliably is to instrument the running code.
> 
> Doing it conservatively still is 100% reliable but has the user occasionally add code that's not needed.

That:

1. still has the user adding dead code

2. will generate endless bug reports (*) on why sometimes the compiler asks for an initialization and sometimes it doesn't

3. (2) will behave in an implementation dependent manner, depending on how thorough the flow analysis is, making for non-portable source code


P.S. I've been doing tech support for compilers for 25 years, and I wish to reduce the workload by designing out things that confuse people!
March 04, 2009
Ary Borenszweig wrote:
> Walter Bright escribió:
>> Ary Borenszweig wrote:
>>> It's not like that. They don't require you to initialize a variable in it's initializer, but just before you read it for the fist time. That's very different.
>>
>> The only way to do that 100% reliably is to instrument the running code.
> 
> Java does it on compile time.

Java is a severely constrained language. Even so, how does it do with this:

Foo f;
if (x < 1) f = new Foo(1);
else if (x >= 1) f = new Foo(2);
f.member();

? (You might ask who would write such, but sometimes the conditions are much more complex, and/or are generated by generic code.)

> If it's done only for local variables then you don't need to instrument the running code.

How about this:

Foo f;
bar(&f);

? Or in another form:

bar(ref Foo f);
Foo f;
bar(f);

Java doesn't have ref parameters.
March 04, 2009
Joel C. Salomon wrote:
> Then the compiler is giving the wrong warning; it ought only to warn
> that the variable is used before it has been set.

That doesn't cover all the cases, nor does it do anything for tracking where a null might come from when going between modules.

And yes, it does require full blown data flow analysis.
March 04, 2009
bearophile wrote:
> Andrei Alexandrescu:
>> extern(C) MaybeNull!(void*) malloc(size_t s);
> 
> Probably I have missed part of the discussion, because wasn't it appreciated by people to use "?" to denote nullables? Something like:
> 
> extern(C) void*? malloc(size_t s);
> 
> Bye,
> bearophile

In C#, Type? is a nullable version of Type, where Type is a value type.

This usage is vaguely similar but sufficiently different that I would not use the same symbols, even in a discussion.
March 04, 2009
BCS wrote:
> Hello Walter,
> 
>> Andrei suggests making a library type work for this rather than a
>> language attribute, but it's still an extra thing that will have to be
>> specified everywhere where used.
>>
> 
> I've considered trying to make a template that compile time enforces Non-null usage but can, with a version flag, be switched to a simple alias/typedef of the internal type. Near zero overhead and possibly just as strong a guard.

It's impossible.

You can create a struct that will throw an exception if you use it uninitialized, or if you try assigning null to it. But the struct cannot require that it is initialized.

You can add a contract that requires the struct be initialized, and put the contract and declaration in a template.
March 04, 2009
Walter Bright escribió:
> Ary Borenszweig wrote:
>> Walter Bright escribió:
>>> Ary Borenszweig wrote:
>>>> It's not like that. They don't require you to initialize a variable in it's initializer, but just before you read it for the fist time. That's very different.
>>>
>>> The only way to do that 100% reliably is to instrument the running code.
>>
>> Java does it on compile time.
> 
> Java is a severely constrained language. Even so, how does it do with this:
> 
> Foo f;
> if (x < 1) f = new Foo(1);
> else if (x >= 1) f = new Foo(2);
> f.member();

Whenever there are branches in code and a variable still doesn't have a value at that point:
 - if all branches assign a value to that variable, from now on the variable has a value
 - if not, at then end of the branches the variable still doesn't have a value

> 
> ? (You might ask who would write such, but sometimes the conditions are much more complex, and/or are generated by generic code.)
> 
>> If it's done only for local variables then you don't need to instrument the running code.
> 
> How about this:
> 
> Foo f;
> bar(&f);
> 
> ? Or in another form:
> 
> bar(ref Foo f);
> Foo f;
> bar(f);
> 
> Java doesn't have ref parameters.

C# does have ref parameters and it also performs this kind of check. I just tried it and it says a parameter can't be passed by reference if it doesn't have a value assigned. So your first example should be an error.

The same should be applied for &references.

(in your first example, if you want to pass f by reference so that bar creates an instance of f, then it should be an out parameter).
March 05, 2009
Christopher Wright wrote:
> BCS wrote:
>> Hello Walter,
>>
>>> Andrei suggests making a library type work for this rather than a
>>> language attribute, but it's still an extra thing that will have to be
>>> specified everywhere where used.
>>>
>>
>> I've considered trying to make a template that compile time enforces Non-null usage but can, with a version flag, be switched to a simple alias/typedef of the internal type. Near zero overhead and possibly just as strong a guard.
> 
> It's impossible.
> 
> You can create a struct that will throw an exception if you use it uninitialized, or if you try assigning null to it. But the struct cannot require that it is initialized.
> 
> You can add a contract that requires the struct be initialized, and put the contract and declaration in a template.

It is possible (I have one hanging out in my code somewhere). It works like this:

struct NonNull(T) if is(T == class)
{
    T value = cast(T) &NullObject!(T);
    ...
    alias value this; // does not compile yet
}

Constructing a null object is not hard - you can do it with static this() code, I forgot exactly how I did it.

But this kind of code is not terribly useful, it initializes "null" objects to correct objects. Instead of getting a hard error, you end up having odd behavior because you're using that object.

One step forward would be to assign all functions in T's vtable to throw-everything (make T a white hole object). Still fields will be changeable.


Andrei
March 05, 2009
Walter Bright wrote:
> Rainer Deyke wrote:
>> I use assertions because they make my life easier.
> 
> Yeah, but you also said they were tedious <g>.

Tedious, but better than the same code without the assertions. Non-nullable types would remove the need for manual assertions.


-- 
Rainer Deyke - rainerd@eldwood.com