From: Dmitry A. Kazakov on
On Tue, 01 Dec 2009 18:52:15 +0100, Georg Bauhaus wrote:

> Dmitry A. Kazakov schrieb:
>> On Tue, 01 Dec 2009 16:02:21 +0100, Georg Bauhaus wrote:
>>
>>> Dmitry A. Kazakov schrieb:
>>>> On Tue, 01 Dec 2009 13:13:29 +0100, Georg Bauhaus wrote:
>>>>
>>>>> Then we could rely on the language: compilers will detect
>>>>> uninitialized variables provided these do not have a pragma/keyword/...
>>>>> to say that uninitialized is what the programmer wants.
>>>>> Some fancy means to tell the compiler that this variable
>>>>> does indeed have a good first value like pragma Import.
>>>>>
>>>>> X : [constant] Car; -- default init,
>>>> The error is here!
>>>>> -- undefined,
>>>>> -- junk bits. Doesn't matter
>>>>> -- *no* pragma Import (Ada, X);
>>>>>
>>>>> begin
>>>>>
>>>>> Spare := X.Tire (5); -- would become illegal,
>>>> Not here!
>>> Why?
>>
>> Because X is illegal right after begin:
>>
>> IF accessing X is illegal THEN the corresponding operation does not belong
>> to the type of X THEN the type of X is not Car. q.e.d.
>
> But the implications of this proof are purely formal,
> and not relevant before X is used.

They are relevant to the declaration of X. It cannot be declared of Car, if
it is not.

> There is no way to perform an operation involving X in
> its own declaration.

But it can be used right after the declaration.

> The difference in views would be that your laws say Don't
> create objects that could be used illegally if there
> were uses that can't be there, though, but for formal reasons.
> Whereas Java's ruling says (at compile time) Your program
> cannot be accepted because this object cannot be in
> a legal state here.

No, Java says, that it self failed to prove that this object is in a state
the programmer might want. This is an absolutely informal statement,
because Java cannot have any idea about what the programmer actually
wanted. The only basis for reasoning might be the object type. But that
tells nothing. So Java speculates that the default constructor is somewhat
worse than copy constructor. Why does it so? Did programmer told this the
compiler? No he didn't. Yes, it might be the case, but then why not to
allow the programmer to say exactly this: do not allow default constructors
for this type? I would even make this a default. E.g. if the programmer
does not explicitly allow default constructors they are forbidden. So

X : T; -- Is always illegal unless I do some actions

>> (Provided, we are talking about a typed language)
>
> I think there is more in what you say than what is covered
> by the words "typed language"?

properly typed language! (:-))

>> And this one:
>>
>> procedure Foo (X : in out Car);
>> ...
>> begin
>> Foo (X);
>> Y := X; -- Is this legal?
>
> Yes, this is legal, because Foo is called with X having been
> assigned a value.

But Foo might read X in its body before updating it. It can leave it
untouched etc.

>> And if Foo were declared as
>>
>> procedure Foo (X : out Car);
>
> We'd have roughly the same as this:
>
> X : Car;
> begin
> X := Foo_as_function; -- now X can be used
>
> I see no operational problem. Is there one?

There is one, Foo might leave X unchanged, unless you introduce further
special rules for out parameters. It will be interesting:

begin
begin
Foo (X);
exception
when Baz =>
null;
end;
Y := X; -- Is this legal?

Ada does not specify what happens with out parameters updated before an
exception gets raised in the body of Foo:

procedure Foo (X : out Car) is
begin
if HALT (p) then
raise Baz; -- Is this legal?
else
X := Merzedes;
end if;
end Foo;

>>>>> Does the phrase "first value" make sense?
>>>> An object shall not have invalid values. All values are valid if the
>>>> language is typed. Enforcing user-defined construction including
>>>> prohibition of certain kinds of construction (e.g. per default constructor)
>>>> is a different story.
>>> If you feed this to a Java compiler you will see how it is done.
>>> The Java compiler will not accept a reference to a variable's
>>> component when the variable may not have been initialized.
>>
>> I consider this model wrong. It is better not to introduce inappropriate
>> values rather than trying to catch them later.
>
> The Java rule works at compile time. No value is introduced at any
> time during compilation. Nothing to catch.

Of course there is something to catch. The compiler has to do this. So the
question is at which cost, how many false positives and negatives it will
find? How scalable is this feature for more elaborated types?

>> Java does not have
>> constrained types, so I can understand why they go this way.
>
> Ehm, I don't see the connection here. Which one is it?
>
> When I declare
>
> X : Some_Type(Some_Constraint);
> begin
> -- X may need further "initilization", and assigments, since
> -- Some_Type is an "open minded" type of a varying nature,
> -- not a fixed value. Its objects accumulate values.

I mean constraints in a wider sense. For example:

Some_Time (<>)

e.g. a subtype that would require explicit initialization.

>> I also think that forward uninitialized declarations represent bad
>> style, e.g.:
>>
>> function Foo (...) return Bar is
>> Result : Bar;
>> begin
>> ...
>> if ... then
>> raise Baz;
>> end if;
>> ...
>> Result := ...;
>> ...
>> return Result;
>> end Foo;
>>
>> I understand the motivation to declare Result uninitialized (because we
>> could leave Foo via an exception), but I don't like this.
>
> But assigning the first value when declaring X won't help
> when the initialization can raise exceptions. How could this change?

I don't follow you. My example illustrated a situation where an
uninitialized value might be an advantage, because one possible outcome of
Foo is exception propagation, in which case leaving Result raw could save
some vital nanoseconds of execution time. I don't buy this.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: John B. Matthews on
In article <wof0ewwkyzy0$.13398rnn8cmje$.dlg(a)40tude.net>,
"Dmitry A. Kazakov" <mailbox(a)dmitry-kazakov.de> wrote:

> On Tue, 01 Dec 2009 18:52:15 +0100, Georg Bauhaus wrote:
>
> > Dmitry A. Kazakov schrieb:
> >> On Tue, 01 Dec 2009 16:02:21 +0100, Georg Bauhaus wrote:
[...]
> >>> If you feed this to a Java compiler you will see how it is done.
> >>> The Java compiler will not accept a reference to a variable's
> >>> component when the variable may not have been initialized.
> >>
> >> I consider this model wrong. It is better not to introduce
> >> inappropriate values rather than trying to catch them later.
> >
> > The Java rule works at compile time. No value is introduced at any
> > time during compilation. Nothing to catch.
>
> Of course there is something to catch. The compiler has to do this.
> So the question is at which cost, how many false positives and
> negatives it will find? How scalable is this feature for more
> elaborated types?

If I may amplify on this, the Java compiler rejects the assignment to
its_color because it's a local variable, which must have an explicit
value before use [1]. Once Tire's constructor completes, the value of
spare.rim_color has the default value null [1]. The compiler need only
check that its_color has been assigned in the current scope [2]. In
Georg Bauhaus' example, the constructor is invoked in a nested scope.

In Ada, I get a warning that '"Spare" is read but never assigned.'

type Tire_Color is (Black, White);
type Tire is record
Rim_Color : Tire_Color;
end record;
Spare : Tire;

If I throw in "for Tire_Color use (Black => 1, White => 2)," the
implicit initial value [3] of Rim_Color is not valid for Tire_Color.
It's "a bounded error to evaluate the value of such an object [4]," and
I get CONSTRAINT_ERROR at run-time.

The Java compiler doesn't warn that spare.rim_color is null by default;
the Ada compiler doesn't warn that Spare.Rim_Color is invalid by
default. In either language, I have to either accept the default initial
values or specify them. I sense I'm missing something.

[1]<http://java.sun.com/docs/books/jls/third_edition/html/typesValues.htm
l#4.12.5>
[2]<http://java.sun.com/docs/books/jls/third_edition/html/defAssign.html#
25979>
[2]<http://www.adaic.com/standards/05rm/html/RM-3-3-1.html>
[3]<http://www.adaic.com/standards/05rm/html/RM-13-9-1.html>
--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>
From: Randy Brukardt on
"Georg Bauhaus" <rm.dash-bauhaus(a)futureapps.de> wrote in message
news:4b150869$0$6732$9b4e6d93(a)newsspool2.arcor-online.net...
....
> If so, and presuming the programming language Ada is very much,
> and explicitly, about storing and manipulating bits in registers,
> memory words, ... of digital computers in a strongly typed fashion:
> In this case I would know the use of being carried away by functional,
> uhm, phantasm, pardon the expression. Rather, why not have Ada turn
> warnings about "uninitialized" variables into a rule like Java's?

Two obvious reasons:
(1) Compatibility. Adding such a rule to Ada would ensure that 98% of
existing Ada code would not compile. This is too fundamental a capability to
take such an incompatibility.
(2) Ada has a design goal of being implementable in a single pass compiler.
A correllary of that goal is that legality rules cannot require flow
analysis. Which means that any rule would have to be essentially the same as
the one Dmitry is proposing: initialization is required in most cases;
delayed initialization is OK only if it is unconditional. One has to think
that using a conditional expression (as in Ada 2012 and in GNAT extensions)
to initialize would be better than constructing some complex rules to allow
initialization such as the one Bob suggested initially.

One can certainly make the argument that the original design of Ada should
have taken this problem more seriously (indeed, I think it is one of the
worst mistakes of the original design team), but that doesn't make it any
more likely that the language will be changed here.

Probably the best that we could do today would be to add an Annex H
restriction against default initialized objects that could have invalid
values. (You'd want to allow well-defined default initialization as occurs
for access types, it doesn't present a problem.) Then a particular project
could require the use of that pragma and thus program in a safer Ada subset.
(We could also help by making it easier to define default initial values for
types other record types.)

Randy.



From: Georg Bauhaus on
On 12/1/09 10:53 PM, John B. Matthews wrote:

> If I may amplify on this, the Java compiler rejects the assignment to
> its_color because it's a local variable, which must have an explicit
> value before use [1]. Once Tire's constructor completes, the value of
> spare.rim_color has the default value null [1]. The compiler need only
> check that its_color has been assigned in the current scope [2]. In
> Georg Bauhaus' example, the constructor is invoked in a nested scope.
>
> In Ada, I get a warning that '"Spare" is read but never assigned.'
>
> type Tire_Color is (Black, White);
> type Tire is record
> Rim_Color : Tire_Color;
> end record;
> Spare : Tire;
>
> If I throw in "for Tire_Color use (Black => 1, White => 2)," the
> implicit initial value [3] of Rim_Color is not valid for Tire_Color.
> It's "a bounded error to evaluate the value of such an object [4]," and
> I get CONSTRAINT_ERROR at run-time.
>
> The Java compiler doesn't warn that spare.rim_color is null by default;
> the Ada compiler doesn't warn that Spare.Rim_Color is invalid by
> default. In either language, I have to either accept the default initial
> values or specify them. I sense I'm missing something.

The Java rule I had been thinking of starts from less emphasis
on what the default initial (Ada) value of a (local) variable
might be, given current Ada rules. Rather, in the sequence
of statements below the compiler would just not accept the reference
to the .Rim_Color component of Spare. The meaning of the
declaration Spare : Tire needs to be understood as slightly changed,
to exclude (or ignore) default initialization.

Spare : Tire;
begin
-- Here, whatever Spare is, or whichever side effects its
-- declaration may have, it is not used between
-- its declaration and the line following the if statement.
-- Therefore, we are free to think of it as something
-- or as nothing, or as something to become a Tire when
-- necessary. A virtual object, perhaps. (Otherwise, use
-- syntax to indicate that there is something important
-- going on in default init; or, for compatibility, when
-- nothing important is going on.)

if Some_Condition then
Spare := Make_a_Tire;
end if;
Its_Color := Spare.Rim_Color; -- illegal


A simple rule would now be a copy of the Java rule which is
quoted below. Just assume that Spare has no value.
Just like the last line is not accepted by SPARK or
by Java (the corresponding Java source line). The warning
which some Ada compilers will issue (that Spare may not have
been assigned a value) is then turned into an error.
As might be expected in Ada, some syntax might be in
order to say that default initialization does
provide an initial value that warrants the safe use of the
variable after the if statement (or is needed for its
side effects, but this is another story, I guess).

Another case is when a declared variable is used in
a declaration of another variable following it,

Spare : Tire;
Another : Tire := Spare; -- might become illegal
begin
...

Illegal unless it is specified that Spare does have
a valid Tire value. Or, oddly, that Another "inherits" the
unknown state of Spare WRT being initialized or not.

This is the Java rule I had in mind. I found it thanks to the
link you have supplied:
"A Java compiler must carry out a specific conservative flow
analysis to make sure that, for every access of a local
variable or blank final field f, f is definitely assigned
before the access; otherwise a compile-time error must occur."


> [2]<http://java.sun.com/docs/books/jls/third_edition/html/defAssign.html#
> 25979>
From: Georg Bauhaus on
On 12/1/09 7:47 PM, Dmitry A. Kazakov wrote:


> It cannot be declared of Car, if it is not.

Which brings us (or me, at least) to the problem of whether or
not the meaning of an Ada program (or a sequential subprogram)
will change if object X really "is" after "begin" or whether
its physical existence is allowed start just before actual
use. :-) X might show its existence through side effects
of its default initialization.
Rather implicit if the effect is important.


>> There is no way to perform an operation involving X in
>> its own declaration.
>
> But it can be used right after the declaration.

Sure, but it isn't used, and the compiler will make sure
it isn't unless it has a value.

> why not to
> allow the programmer to say exactly this: do not allow default constructors
> for this type? I would even make this a default. E.g. if the programmer
> does not explicitly allow default constructors they are forbidden. So
>
> X : T; -- Is always illegal unless I do some actions

Or maybe this could mean that if there is no explict specification
of actions associated with the declaration, X must be
considered uninitialized in the following text.
Some syntax might be nice to have.


> Ada does not specify what happens with out parameters updated before an
> exception gets raised in the body of Foo:
>
> procedure Foo (X : out Car) is
> begin
> if HALT (p) then
> raise Baz; -- Is this legal?
> else
> X := Merzedes;
> end if;
> end Foo;

If we were to integrate exceptions into normal control
flow like Java does? Here is what Java does when the
constructor may raise an exception (taking the role of Foo
above):

if (some_condition) {
try {
spare = new Dummy.Tire();
} catch (Exception e) {
;
}
// this line not accepted by a Java compiler:
its_color = spare.rim_color; // <-----
}
// this line not accepted by a Java compiler:
its_color = spare.rim_color; // <-----


> Of course there is something to catch. The compiler has to do this. So the
> question is at which cost, how many false positives and negatives it will
> find? How scalable is this feature for more elaborated types?

From the Java point of view, there are no false positives
or negatives. The rule is pretty clear, I think.
Assuming that Ada programmers will be able to say
"is initialized" for just a variable declaration, there
should not be any false positives or negatives.
There could be more reliance on the programmer, though,
not sure.

> My example illustrated a situation where an
> uninitialized value might be an advantage, because one possible outcome of
> Foo is exception propagation, in which case leaving Result raw could save
> some vital nanoseconds of execution time. I don't buy this.

Whether the out mode variable had been initialized in the
body or not, the exceptional situation might have destroyed
the value of the out mode parameter. A variable would not
be considered initialized, I'd think, in an exception handler,
unless the programmer says so (or assigns a value).