From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,9a586954b11ae008 X-Google-Attributes: gid103376,public From: bobduff@world.std.com (Robert A Duff) Subject: Re: What Happened While I Wasn't Looking? Date: 1997/04/03 Message-ID: X-Deja-AN: 230462116 References: <1997Apr2.202514.1843@nosc.mil> Organization: The World Public Access UNIX, Brookline, MA Newsgroups: comp.lang.ada Date: 1997-04-03T00:00:00+00:00 List-Id: In article <1997Apr2.202514.1843@nosc.mil>, Charles H. Sampson wrote: >The Prologue, being information fascinating to all and necessary to un- >derstand the author's position before reading The Questions: OK, here's my Prologue: I was on the Ada 9X language design team, and I wrote much of chapter 13. > The majority of my career has been spent in maintenance program- >ming, with almost all of that in FORTRAN and CMS-2. Based on my experi- >ence, the single greatest cause of maintenance problems (bugs) and >enhancement difficulties is the overlay. Ah, but overlays are *not* a big cause of maintenance problems in Ada 83 or Ada 95. This isn't because they're forbidden -- it's because they're not needed most of the time. >... When I taught Ada 83 I pointed out this issue and used it as a >selling point, saying that overlaying was impossible in a non-erroneous >Ada program. I don't understand how this can be a benefit. Ada compilers are not required to detect erroneous things (neither at compile time nor at run time). So how can it be beneficial (in terms of eliminating bugs) for the RM to declare something erroneous? Analogy: Suppose you write a signed integer expression that overflows. In C, you get unpredictable results (exactly what Ada calls erroneous). In Ada, you get a run-time error. In Lisp, you get the right answer. It seems to me that, as far as preventing bugs goes, with respect to this particular issue, C is worst, Ada is better (because it detects the error), and Lisp is better still (because it prevents the error). [Before somebody starts a three-way language war -- please note that I said "with respect to this particular issue"!] Erroneousness/unpredictability seems like the worst possible semantics (from the point of view of getting rid of bugs -- from the point of view of efficiency, erroneousness can be a good thing, which is why it exists in the first place). Anyway, IMHO, a language definition should not try to "prevent evil" so much as "encourage good". Example: I've seen cases in FORTRAN 66 (I haven't used FORTRAN in a long time!), where EQUIVALENCE was used for space efficiency. Two unrelated subroutines wanted to use the same storage for totally unrelated purposes, and the programmer ensured that this storage wasn't being used at the same time. Well, in Ada (or Pascal or C or ...), you just use local variables, which get allocated on the stack, for this purpose. You end up reusing the same memory, but in a much safer, more maintainable way. The reason people do it this way in Ada is because Ada has this nice feature of local variables -- not because of some admonishment against Address clauses. >... I went on to extol the virtues of Unchecked_conversion, in >particular the point that it makes the "aliasing" obvious at the point >where it is being used. Well, unchecked conversions can be erroneous, too, in some cases. Also, note that unchecked_conv can create aliases, in some cases. The obvious case is that you can convert from an address or access value to an access value, which, if it works at all, which it does on most compilers for most data types, creates an alias. It seems to me that these kinds of aliases are just as damaging as the ones created by Address clauses. A lesser-known case is 13.9(12), "An implementation may return the result of an unchecked conversion by reference, ...". This was also true in Ada 83, by virtue of an AI (though the original RM83 didn't say so). The reason for this rule is efficiency. Furthermore, implementations are *encouraged* to do the by-ref thing, in 13.9(15). It can create aliases, which is why the explicit permission is needed, since functions normally return their results by copy. >...Now I will be forced to admit that the beast is >alive and kicking, at least to some degree, in Ada 95. Methinks you exaggerate. Just because the RM has changed, doesn't mean everybody is going to go out and sprinkle address clauses all over their code. The goal of the language designer should be to provide enough safe features, that Address clauses and unchecked conversions and whatnot are rarely needed. (E.g. if space efficiency is the goal, give the programmer local variables, variant records, class-wide types, etc. If the goal is to break the typing system in an efficient manner, then, well, there's not much you can do -- let the programmer worry about it.) >The Questions: > > My primary question is, why was this change made? First of all, the change was not to simply eliminate the Ada 83 rule. The rule was replaced by 13.3(13), which says almost the same thing, and certainly lets one know of the potential evils of this feature. I think the change was made for these reasons: (1) It's hard to formally define what an "overlay" is. I mean, suppose you use an Address clause to put an Ada variable at a location that is some sort of I/O control register. Is that an overlay? In some sense, it is -- you're overlaying the Ada variable with a non-Ada "variable". But this is exactly what Address clauses are *for*. Does "overlay" only mean overlaying two Ada variables? Why? (2) In practise, although Ada 83 programmers didn't use address_clauses much, when they did, they sometimes used them to make overlays, and their programs worked fine (albeit non-portably). It seems silly to insist that common practise is erroneous. The real rule is that you have to know what sort of code your compiler is generating, and that's what 13.3(13) says. (3) Other features are just as "evil" (for example unchecked conversion of access values). Why single out address clauses? (4) Overlays can be useful (but you have to be careful, if you're going to avoid maintenance headaches). (5) It's possible to encapsulate one's use of overlaid storage. (6) The language design team, and the reviewers, spent most of their time worrying about features outside chapter 13. This was also true of the Ada 83 RM, and partly explains why chapter 13 was and is more buggy than other parts of the RM. >...Was the negative >impact on maintenance considered? I haven't found anything on the issue >in the Rationale. I've misplaced my copy of the AARM, but I wouldn't >expect to find anything on it there. There's quite a bit of discussion in the AARM about how Address clauses can cause trouble. There is no explicit list of reasons for the change. >... Robert Dewar has pointed out that >there are efficiency gains coming from never having to do a copy when >changing from one view to another. Was that the only consideration? > >The Epilogue, being divers observations on the issue: > > The saddest aspect of this mistaken change (my opinion, obviously) >is that it doesn't adequately support overlaying. Even if a compiler >implements the recommended level of support (RM 13.3(12-19)) it is only >required to do the right thing for the one of the overlaid data whose >address has been specified (RM 13.3(19)). If you make the other variable "aliased", then you'll be OK. >...To the response that a com- >piler implemented to minimally satisfy the requirement would be very >poor, consider the example used to demonstrate why overlays through ad- >dress clauses couldn't work in Ada 83: The overlaid data come from dif- >ferent package specifications. A conventional Ada compiler, retrieving >information about the packages from its library, would probably find >that only one of the data should be treated conservatively. (The much >maligned CMS-2, which raised aliasing to undreamed of heights, had a >rule preventing this kind of thing.) I suppose it would be possible >after compiling the second package specification (the one that contains >the address clause) to modify the library information for the first >package, but how many compilers do this kind of thing? I don't think any such thing is possible, even in theory. That's why we talk about "erroneous", rather than "illegal". Consider: X: T; procedure P(A: Address) is Y: T2; for Y'Address use A; begin ... end P; P(X'Address + Ident_Int(17)); ...where the declaration of X, the procedure body P, and the call to P, might be in three different compilation units, and neither of the body of P and the call to P have visibility upon each other. > Robert has written that he always uses pragma(volatile) on both >data to make sure the overlay works. That should do the trick, but I >see two problems; there may be more. The first is that code that reads >the overlaid data becomes overly conservative, fetching them every time >rather than ever using an in-register value. (This seems to weaken the >claim of efficiencies made for the feature.) The second is that it >amounts to lying to the compiler and, more important, to the maintenance >programmer. According to RM C.6(1), pragma(volatile) is used to "con- >trol the use of shared variables". While it might seem like a harmless >white lie to tell the maintenance programmer that a non-shared variable >is shared, in my experience any lie in programming eventually comes back >to haunt you, usually at the most inopportune moment. > > It's interesting to note that GNAT handles the two package example >"right". My guess is that that's a result of GNAT's treating package >specifications like include files. On the other hand, GNAT (PC version >3.07) allows one of the overlaid objects to be a constant and the other >to be variable. Furthermore, the generated code allows the value of the >constant to be changed. This is clearly a bug, either in GNAT or the >language specification. It is not prohibited in RM 13.3. Is it prohib- >ited elsewhere? No, except that 13.3(13) allows the compiler to declare this case erroneous. I think the idea is that you don't clutter low-level features with all kinds of safety-related rules. If you want safety, use all the nice features in chapters 1 to 12. If you use something in chapter 13 that's unsafe, encapsulate it, and be careful. - Bob