From: Dmitry A. Kazakov on 1 Dec 2009 13:47 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 1 Dec 2009 16:53 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 1 Dec 2009 18:51 "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 1 Dec 2009 19:32 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 1 Dec 2009 20:13
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). |