December 30, 2009
Steven Schveighoffer wrote:
> On Mon, 28 Dec 2009 21:52:24 -0500, Don <nospam@nospam.com> wrote:
> 
>> Steven Schveighoffer wrote:
>>> Whether you like it or not, the runtime must initialize it to something, even if the value means "uninitialized".
>>
>> The key question is: is it valid to read an uninitialized variable? I would hope the compiler would flag that as an error. The use of signalling NaNs was based on the assumption that you should *never* be reading uninitialized variables.
>>
>> A consequence is that you should also NEVER read float.init!
> 
> I am not even close to a floating point expert.  In fact, I try to avoid them like the plague.  All I want to do is verify that the bit pattern being written to a memory location is the bit pattern I expect to be written.  How do I do that without comparing what I expect to what I wrote?

The thing is, if you've written something to a memory location, it's not uninitialized any more.

>>> That value should always be consistent.  There has to be a way to verify that the value was set properly.  Otherwise, how do you verify things
>>> like communication protocols or expectations for runtime functions?
>>
>> I think that sending an uninitialized variable down a communication channel is always a bug.
> 
> Just wait, someone will define it :)  If I want to verify that it's any value *but* T.init, how do I do that?  And I don't think using <>=!@%$ to do it is worth the trouble.
> 
>>> It has to be a bit pattern, because all other T.init values, including those of aggregates which might contain floating points, are bit patterns.
>>>
>>>> Consider this code:
>>>>
>>>> T x;
>>>> if (x==T.init) { writefln("x is uninitialized"); }
>>>>
>>>> Is this code valid? You are reading the value of an uninitialized variable.
>>>  Correct that to:
>>>  T x;
>>> if(x is T.init) {writefln("x is uninitialized"); }
>>>  Then I think it is valid.  I don't care if == is hijacked away from bitwise comparison, in fact, it is necessary in many cases.  But there should be a way to do bitwise comparison for verification, and 'is' fits the bill perfectly.
>>
>> That does preclude a compiler from ever giving you errors/warnings about using uninitialized variables. I think it's wrong.
> 
> I'm not using it in every-day code (although I don't think it's wrong to do so), I'm using it for asserts and unit tests.  I'm asserting that what's happening is what I expect is happening.  To make this unreasonably difficult makes no sense to me.  To say "is works on everything but floating point initializers" sounds ridiculously inconsistent.  Maybe preventing it is the Right Thing to do, but it is possible to do, so what harm can it cause?  It wouldn't even cause an exception.
> 
> Note that I don't think == should change behavior, just 'is'.

I think we could get the behaviour you want by changing the definition of T.init. I think it should ALWAYS be a bug to read an uninitialized variable; but it should be OK to have:
T x = T.init;
if (x == T.init) ...

Otherwise, there's no point in making T.init available.
December 30, 2009
Don wrote:
<snip>
> I think we could get the behaviour you want by changing the definition of T.init. I think it should ALWAYS be a bug to read an uninitialized variable;
<snip>

All variables in D are initialized, unless overridden with a void initializer.  But what would "ALWAYS be a bug" mean?

(a) Trigger a compile-time error?

While some C(++) compilers will warn you if they catch you trying to read a variable before it's been set, they cannot be perfect at doing so.  That's probably one reason that D takes the simpler route.

(b) Trigger a run-time error?

This would be a performance hit, as you'd need
- extra working memory, as a bit to keep track of whether each variable is set
- extra code to set and check this bit when a variable is used
- extra CPU cycles to execute this extra code

I'd be inclined to save this kind of behaviour for scripting languages.

(c) Trigger undefined behaviour?

I can't see any real way of making it undefined short of going back to the C way.

One of D's reasons for initializing all variables is so that bugs caused by reading variables before they're otherwise set are easier to diagnose, since the behaviour doesn't change with every execution.

Another reason I can see is to avoid memory trample, possibly also GC horrors, caused by leaving pointers accidentally pointing somewhere and then trying to use them.  While you could get around this by defining that reference types are initialized to null and value types are not initialized, it's simpler to define that all variables are initialized.

A further complication is that, in a struct or class, some members may have default initializers set and others not.  Initializing a variable of struct or class type is much simpler if the fact of being initialized applies to the whole struct rather than some of its members.

(d) Be perfectly legal and defined, yet deemed immoral?

Stewart.
December 30, 2009
Stewart Gordon wrote:
> Don wrote:
> <snip>
>> I think we could get the behaviour you want by changing the definition of T.init. I think it should ALWAYS be a bug to read an uninitialized variable;
> <snip>
> 
> All variables in D are initialized, unless overridden with a void initializer.  But what would "ALWAYS be a bug" mean?
> 
> (a) Trigger a compile-time error?
> 
> While some C(++) compilers will warn you if they catch you trying to read a variable before it's been set, they cannot be perfect at doing so.  That's probably one reason that D takes the simpler route.
> 

Yes, but it can be a QOI issue. That is, it's always a bug, the compiler will do the best it can to detect it at compile time, but it isn't always possible. Proof:

bool arbitrarily_difficult() {}
int main()
{
  int x;
  if (arbitrarily_difficult()) x=7;
  return x;
}

So it's clearly an NP-hard problem.

So we have run-time initialisation to catch the cases that are too hard for the compiler.


> (b) Trigger a run-time error?
> 
> This would be a performance hit, as you'd need
> - extra working memory, as a bit to keep track of whether each variable is set
> - extra code to set and check this bit when a variable is used
> - extra CPU cycles to execute this extra code
> 
> I'd be inclined to save this kind of behaviour for scripting languages.
> 
> (c) Trigger undefined behaviour?
> 
> I can't see any real way of making it undefined short of going back to the C way.
> 
> One of D's reasons for initializing all variables is so that bugs caused by reading variables before they're otherwise set are easier to diagnose, since the behaviour doesn't change with every execution.
> 
> Another reason I can see is to avoid memory trample, possibly also GC horrors, caused by leaving pointers accidentally pointing somewhere and then trying to use them.  While you could get around this by defining that reference types are initialized to null and value types are not initialized, it's simpler to define that all variables are initialized.
> 
> A further complication is that, in a struct or class, some members may have default initializers set and others not.  Initializing a variable of struct or class type is much simpler if the fact of being initialized applies to the whole struct rather than some of its members.
> 
> (d) Be perfectly legal and defined, yet deemed immoral?

The question is, are they initialized solely in order to help detect bugs, or are they initialized so that you can use them?



1 2
Next ›   Last »