* Call by reference vs. call by value @ 1996-07-20 0:00 Christopher Felaco 1996-07-20 0:00 ` James A. Krzyzanowski ` (2 more replies) 0 siblings, 3 replies; 29+ messages in thread From: Christopher Felaco @ 1996-07-20 0:00 UTC (permalink / raw) I came across a problem with a procedure that resembled the following: type Simple_Vector is array (1 .. 2) of Float; procedure Vector_Manipulation (X : in Simple_Vector; Y : out Simple_Vector) is begin Y (1) := X (1) + X (2); Y (2) := X (1) * X (2); end Vector_Manipulation; I was told that calling this procedure with the same in and out parameters as follows would give unexpected results: Vector_Manipulation (X => Test_Vector, Y => Text_Vector); The reason is that both parameters may be passed by reference, hence modifying Y in the first line, modifies X as well, and the value of X used in the second line is not what was desired. I was surprised by this, it sounds like a typical problem in C or C++, not something likely to happen in Ada. So I did some research and concluded from section 6.2 of the Ada 95 RM that it is entirely up to the compiler to determine how arrays of simple elementary values are called. I recall also reading that ACT got itself in a bit of trouble by changing the behavior of gnat to use call by reference for all records. My reasons for posting this are twofold. The first reason is to verify that this is correct, and see if anyone else has been bitten by this particular undefined behavior. The second reason is to express some disappointment. I think in this area, C++ has a big advantage in that it forces the programmer to be aware of whether a function uses call-by-refernce of call-by-value semantics. This is somewhat of a pain at times, but at least it is obvious and clearly defined. I had no idea that Ada's behavior was undefined and wrongly assumed that in parameters were always passed by value. I realize that the solution to this problem in this case is to simply make one assignment using an array aggregate, or change the procedure to a function. I have recommended this course of action. I still think that this type of error should not occur. In many situations, the solution may not so simple. Ada's behavior should not be so "implementation dependent". - Chris ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 Call by reference vs. call by value Christopher Felaco @ 1996-07-20 0:00 ` James A. Krzyzanowski 1996-07-20 0:00 ` Robert Dewar 1996-07-20 0:00 ` Robert Dewar 1996-07-21 0:00 ` Robert A Duff 2 siblings, 1 reply; 29+ messages in thread From: James A. Krzyzanowski @ 1996-07-20 0:00 UTC (permalink / raw) Christopher Felaco (felaco@egr.uri.edu) wrote: : I came across a problem with a procedure that resembled the following: : type Simple_Vector is array (1 .. 2) of Float; : procedure Vector_Manipulation (X : in Simple_Vector; : Y : out Simple_Vector) is : begin : Y (1) := X (1) + X (2); : Y (2) := X (1) * X (2); : end Vector_Manipulation; : I was told that calling this procedure with the same in and out : parameters as follows would give unexpected results: : Vector_Manipulation (X => Test_Vector, Y => Text_Vector); ^ Test_Vector I think you have a typo and meant that both actual parameters were the same variable. I just recently answered this exact question for a colleague. Here is a copy of my reply... I don't know if this is it, but check out LRM 6.2... paragraph (1) - "...The value of a variable is said to be updated when an assignment is performed to the variable, and also (indirectly) when the variable is used as actual parameter of a subprogram call...that updates its value" paragraph (7) - "...an implementation may achieve these effects by reference, that is, by arranging that every use of a formal parameter (to read or to update its value) be treated as a use of the associated actual parameter, throughout the execution of the subprogram call." Since the out parameter is the same variable as the in parameter, if the compiler writers chose to use "reference" for both the in and out parameter then the value of the in parameter would change everytime the out parameter changed. Sounds hokie but they could probably get away with it with the language referenced above. -- Not necessarily the opinion of the company... -- --------------------------------------------------------------------------- James A. Krzyzanowski - Senior Software Engineer - AFATDS Magnavox Electronic Systems Company * Fort Wayne, IN 46808 * (219) 429-6446 Internet: jakrzy@most.fw.hac.com * AOL: JimShiz@AOL.com * MOST: jakrzy@most "I'd rather be right than politically correct !!!" - Rush is Right --------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 ` James A. Krzyzanowski @ 1996-07-20 0:00 ` Robert Dewar 0 siblings, 0 replies; 29+ messages in thread From: Robert Dewar @ 1996-07-20 0:00 UTC (permalink / raw) James said "Since the out parameter is the same variable as the in parameter, if the compiler writers chose to use "reference" for both the in and out parameter then the value of the in parameter would change everytime the out parameter changed. Sounds hokie but they could probably get away with it with the language referenced above." Well passing arrays by reference may seem hokie (isn't that word spelled hoky -- don't really know, it is not in my dictionary :-), but it is quite usual, and is certainly what you want for really large arrays. Most Ada programs will pass all arrays by reference, as will most Fortran programs. To me, a compiler that passed all arrays by copy would be *really* broken -- at least we didnt' go that far in GNAT! ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 Call by reference vs. call by value Christopher Felaco 1996-07-20 0:00 ` James A. Krzyzanowski @ 1996-07-20 0:00 ` Robert Dewar 1996-07-21 0:00 ` Robert A Duff ` (3 more replies) 1996-07-21 0:00 ` Robert A Duff 2 siblings, 4 replies; 29+ messages in thread From: Robert Dewar @ 1996-07-20 0:00 UTC (permalink / raw) Chris said This is somewhat of a pain at times, but at least it is obvious and clearly defined. I had no idea that Ada's behavior was undefined and wrongly assumed that in parameters were always passed by value. Well you can always get bitten by not knowing a language rule that is clear, and you can always be surprised if you wrongly assume something! Ada's behavior is not undefined, it is implementation defined (there is an important difference!) It is quite odd to assume that all IN parameters are passed by value, do you really expect million element arrays to be copied when they are passed to procedures, I think not. I realize that the solution to this problem in this case is to simply make one assignment using an array aggregate, or change the procedure to a function. I have recommended this course of action. I still think that this type of error should not occur. In many situations, the solution may not so simple. Ada's behavior should not be so "implementation dependent". Here is a case where the language semantics is non-deterministic for efficiency reasons. To force call by value in all cases would be clearly unaccepable for large arrays. On the other hand, to force call by reference is also inefficient on some machines. Yes, the semantics would be cleaner if there were no implementation dependence here, but efficiency issues certainly cannot be ignored. The question is, does someone who knows Ada get into trouble with this rule? Clearly if you don't know a rule in the language you can always get into trouble, you can't expect Ada to automatically correct your misconceptions about the language. This is certainly a lesson that you need to know a language well to avoid trouble, but it is not convincing that there is a problem here. Actually, I think most people who make a mistake here make a mistake the other way round, they assume that arrays (and even records) have to be passed by reference. The same thing happens in Fortran (which shares Ada's approach of leaving it up to the implementation to decide whether to pass by copy or reference) -- people often assume Fortran requires by-reference. You actually remember the GNAT situation wrong, and if you remembered it right, it would have reminded you of the issue. We changed record passing from being by reference for large records to being always by value, as you would like to see. This turned out to be completely unacceptable in some situations because of severe degradation of performance -- so this is a nice object lesson that your recommendation is infeasible! P.S. we also got quite a few complaints of regressions that turned out to be people assuming that records were passed by reference, and their programs depended on reference semantics! P.P.S. In C++ you can't pass arrays anyway so the issue does not arise, so it is wrong to say that the mechanism for this is defined in C++! If you cold pass arrays in C or C++, then the same problem would arise. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 ` Robert Dewar @ 1996-07-21 0:00 ` Robert A Duff 1996-07-21 0:00 ` Robert Dewar 1996-07-22 0:00 ` Felaco ` (2 subsequent siblings) 3 siblings, 1 reply; 29+ messages in thread From: Robert A Duff @ 1996-07-21 0:00 UTC (permalink / raw) In article <dewar.837901109@schonberg>, Robert Dewar <dewar@cs.nyu.edu> wrote: >P.S. we also got quite a few complaints of regressions that turned out to be >people assuming that records were passed by reference, and their programs >depended on reference semantics! But this is proof that the implementation-definedness is indeed a bug-causing problem. I'm convinced there must be a better solution that does not introduce huge amounts of copying. - Bob P.S. In thinking about this issue, one point is that for remote procedure calls, you pretty much *have* to pass by copy, even for very large things. So a rule saying "all arrays are passed by reference" or "all arrays that are not statically known to be small are passed by reference (for some definition of small)" would break the Distributed Systems Annex. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-21 0:00 ` Robert A Duff @ 1996-07-21 0:00 ` Robert Dewar 1996-07-22 0:00 ` Robert A Duff 0 siblings, 1 reply; 29+ messages in thread From: Robert Dewar @ 1996-07-21 0:00 UTC (permalink / raw) Bob Duff said "P.S. In thinking about this issue, one point is that for remote procedure calls, you pretty much *have* to pass by copy, even for very large things. So a rule saying "all arrays are passed by reference" or "all arrays that are not statically known to be small are passed by reference (for some definition of small)" would break the Distributed Systems Annex." If you want to think more about this issue, worry about packed arrays too. Requiring call by reference would mean that ALL packed arrays have to be passed using general bit pointers, which would be unacceptably inefficient in the normal case where slices are not passed. Same thing for records, all records would have to be passed by bit address, just in case the record you are passing is a field in a rcord with a record rep clause. This is not an easy problem to solve -- if there were a simple solution, I think it might have been found by now, but there doesn't seem to be one. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-21 0:00 ` Robert Dewar @ 1996-07-22 0:00 ` Robert A Duff 1996-07-23 0:00 ` Peter Amey 0 siblings, 1 reply; 29+ messages in thread From: Robert A Duff @ 1996-07-22 0:00 UTC (permalink / raw) In article <dewar.837995690@schonberg>, Robert Dewar <dewar@cs.nyu.edu> wrote: >If you want to think more about this issue, worry about packed arrays >too. Requiring call by reference would mean that ALL packed arrays have >to be passed using general bit pointers, which would be unacceptably >inefficient in the normal case where slices are not passed. Same >thing for records, all records would have to be passed by bit address, >just in case the record you are passing is a field in a rcord with a >record rep clause. Pascal solved this problem in a simple (but rather ugly) way: It's illegal to pass a component of a packed array or record as a parameter. The *programmer* must declare a temp variable, and make a copy. And Pascal doesn't have slices. I suspect part of the reason for Ada's rules (which of course predate the DS annex by a decade) is to avoid the ugliness of the Pascal rule. Unfortunately, the Ada rule introduces an implementation dependence that I find uncomfortable. - Bob ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-22 0:00 ` Robert A Duff @ 1996-07-23 0:00 ` Peter Amey 1996-07-23 0:00 ` Robert Dewar ` (2 more replies) 0 siblings, 3 replies; 29+ messages in thread From: Peter Amey @ 1996-07-23 0:00 UTC (permalink / raw) This is another Ada feature well covered by the SPARK subset. The rules of SPARK (which are checked by the SPARK Examiner) prohibit all cases of aliasing where program meaning might be affected by the parameter passing mechanism used. A SPARK program has copy-in, copy-out semantics regardless of the compiler used to compile it. Peter ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-23 0:00 ` Peter Amey @ 1996-07-23 0:00 ` Robert Dewar 1996-07-24 0:00 ` Robert A Duff 1996-07-23 0:00 ` Robert A Duff 1996-07-24 0:00 ` Richard A. O'Keefe 2 siblings, 1 reply; 29+ messages in thread From: Robert Dewar @ 1996-07-23 0:00 UTC (permalink / raw) Peter Amey said "This is another Ada feature well covered by the SPARK subset. The rules of SPARK (which are checked by the SPARK Examiner) prohibit all cases of aliasing where program meaning might be affected by the parameter passing mechanism used. A SPARK program has copy-in, copy-out semantics regardless of the compiler used to compile it." Right (or for that matter you can say a SPARK program has by reference semantics regardless of the compiler used to compile it [for the array case] :-) SPARK itself is too restrictive for a lot of general use purposes, but the kinds of restrictions that it has are worth considering individually in any coding standard. I actually like the Fortran rule as to exactly what is and what is not allowed in Fortran. It is as follows: if there are two aliased paths to the same object, it is erroneous (to use the Ada terminology) to assign to the object via either path. That's a very well defined rule. I much prefer it to eithe the bogus rule in the Ada 83 RM (erroneous if effect differs, but effect never defined), or to the Ada 95 RM rule that programs are simply non-determinisitc if their effects depend on the mechanism. I think the above (Fortran) rule is he one you should have in mind wihile programming (it is a bit more restrictive than the Ada 95 approach, since there are progrmas whose effect does not depend on the mechanism, but which violate this rule), but in practice it is perfectly workable. You can now if you like follow this up with specific coding rules designed to prevent this from occurring (e.g. forbidding globally visible objects to be passed as out or in out parameters to routines that can see the global, or forbidding the same object from being passed as two separate parameters if one of them is out or in out). ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-23 0:00 ` Robert Dewar @ 1996-07-24 0:00 ` Robert A Duff 0 siblings, 0 replies; 29+ messages in thread From: Robert A Duff @ 1996-07-24 0:00 UTC (permalink / raw) In article <dewar.838132574@schonberg>, Robert Dewar <dewar@cs.nyu.edu> wrote: >I actually like the Fortran rule as to exactly what is and what is not >allowed in Fortran. It is as follows: > > if there are two aliased paths to the same object, it is erroneous (to use > the Ada terminology) to assign to the object via either path. > >That's a very well defined rule. I much prefer it to eithe the bogus rule >in the Ada 83 RM (erroneous if effect differs, but effect never defined), >or to the Ada 95 RM rule that programs are simply non-determinisitc if >their effects depend on the mechanism. I think both the Ada rule and the Fortran rule are pretty ugly. The problem is that you can't pin the error down to one piece of code. Is it the fault of the caller, or the callee? There's nothing in the spec that gives any sort of contractual information about which side has to do what. >You can now if you like follow this up with specific coding rules designed >to prevent this from occurring (e.g. forbidding globally visible objects >to be passed as out or in out parameters to routines that can see the >global, or forbidding the same object from being passed as two separate >parameters if one of them is out or in out). But at the call site, I can't tell which global variables are visible to the callee, without looking at its body. So not only can I get bugs, but the bugs can be triggered by porting the code, or by turning on the optimizer, which is more painful than a normal run-of-the-mill bug. I must admit it's not *that* big of a deal, since you don't normally have a lot of global variables lying around. - Bob ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-23 0:00 ` Peter Amey 1996-07-23 0:00 ` Robert Dewar @ 1996-07-23 0:00 ` Robert A Duff 1996-07-27 0:00 ` Peter Morris 1996-07-24 0:00 ` Richard A. O'Keefe 2 siblings, 1 reply; 29+ messages in thread From: Robert A Duff @ 1996-07-23 0:00 UTC (permalink / raw) In article <Pine.SUN.3.91.960723082703.22250A-100000@erlang.praxis.co.uk>, Peter Amey <pna@erlang.praxis.co.uk> wrote: >This is another Ada feature well covered by the SPARK subset. The rules >of SPARK (which are checked by the SPARK Examiner) prohibit all cases >of aliasing where program meaning might be affected by the parameter >passing mechanism used. A SPARK program has copy-in, copy-out semantics >regardless of the compiler used to compile it. Right, but you could equally well say that a SPARK program has by-ref semantics. SPARK ensures that the two are equivalent by avoiding aliasing, as you said, and also by avoiding exception handlers. - Bob ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-23 0:00 ` Robert A Duff @ 1996-07-27 0:00 ` Peter Morris 1996-07-28 0:00 ` Robert A Duff 0 siblings, 1 reply; 29+ messages in thread From: Peter Morris @ 1996-07-27 0:00 UTC (permalink / raw) bobduff@world.std.com (Robert A Duff) wrote: >In article <Pine.SUN.3.91.960723082703.22250A-100000@erlang.praxis.co.uk>, >Peter Amey <pna@erlang.praxis.co.uk> wrote: >>This is another Ada feature well covered by the SPARK subset. The rules >>of SPARK (which are checked by the SPARK Examiner) prohibit all cases >>of aliasing where program meaning might be affected by the parameter >>passing mechanism used. A SPARK program has copy-in, copy-out semantics >>regardless of the compiler used to compile it. >Right, but you could equally well say that a SPARK program has by-ref >semantics. SPARK ensures that the two are equivalent by avoiding >aliasing, as you said, and also by avoiding exception handlers. >- Bob The occam programming language which I have been using for distributed control systems also prohibits aliasing. As this seems to be a simple and efficient way to prevent the problems that aliasing can cause I wonder if it might not be a good idea for the full version of ADA95 to prohibit aliasing. > ... and also by avoiding exception handlers. Would it be sensible for an implementation of ADA95 to prohibit aliasing yet allow exception handlers? Peter Morris ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-27 0:00 ` Peter Morris @ 1996-07-28 0:00 ` Robert A Duff 0 siblings, 0 replies; 29+ messages in thread From: Robert A Duff @ 1996-07-28 0:00 UTC (permalink / raw) In article <4tdat5$3jb@clams.camtech.com.au>, Peter Morris <peterm@senet.com.au> wrote: >The occam programming language which I have been using for distributed >control systems also prohibits aliasing. As this seems to be a simple >and efficient way to prevent the problems that aliasing can cause I >wonder if it might not be a good idea for the full version of ADA95 to >prohibit aliasing. SPARK uses a VERY restricted subset of Ada. Detecting aliasing in full Ada is neither simple nor efficient. - Bob ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-23 0:00 ` Peter Amey 1996-07-23 0:00 ` Robert Dewar 1996-07-23 0:00 ` Robert A Duff @ 1996-07-24 0:00 ` Richard A. O'Keefe 2 siblings, 0 replies; 29+ messages in thread From: Richard A. O'Keefe @ 1996-07-24 0:00 UTC (permalink / raw) Peter Amey <pna@erlang.praxis.co.uk> writes: >This is another Ada feature well covered by the SPARK subset. The rules >of SPARK (which are checked by the SPARK Examiner) prohibit ... It sounds as though the SPARK Examiner is a static checker something along the lines of the PFORT checker only more so. I do hope this is something a CS department in an era of 10% education funding cuts can get at an academic price it can afford to pay... Can you offer a thumbnail sketch of what the SPARK subset is? -- Fifty years of programming language research, and we end up with C++ ??? Richard A. O'Keefe; http://www.cs.rmit.edu.au/~ok; RMIT Comp.Sci. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 ` Robert Dewar 1996-07-21 0:00 ` Robert A Duff @ 1996-07-22 0:00 ` Felaco 1996-07-22 0:00 ` Robert A Duff 1996-07-22 0:00 ` Robert Dewar 1996-07-22 0:00 ` Karl Cooper {46901} 1996-07-30 0:00 ` Felaco 3 siblings, 2 replies; 29+ messages in thread From: Felaco @ 1996-07-22 0:00 UTC (permalink / raw) dewar@cs.nyu.edu (Robert Dewar) writes: > > Chris said > > This is somewhat of a pain > at times, but at least it is obvious and clearly defined. I had no idea > that Ada's behavior was undefined and wrongly assumed that in parameters > were always passed by value. > > Well you can always get bitten by not knowing a language rule that is > clear, and you can always be surprised if you wrongly assume something! > Ada's behavior is not undefined, it is implementation defined (there is > an important difference!) > > It is quite odd to assume that all IN parameters are passed by value, do > you really expect million element arrays to be copied when they are passed > to procedures, I think not. Of course I have to admit ignorance here. I never really thought about it in Ada. For some reason I assumed that the compiler would in some way ensure that this type of error would not occur, either by passing the array by value, or reordering the assignments so the problem would not occur. I realize I expected too much. > Here is a case where the language semantics is non-deterministic for > efficiency reasons. To force call by value in all cases would be clearly > unaccepable for large arrays. On the other hand, to force call by > reference is also inefficient on some machines. Yes, the semantics would > be cleaner if there were no implementation dependence here, but efficiency > issues certainly cannot be ignored. Well, I guess I never conceived a machine might exist where call-by- value for anything bigger than a word is more efficient than call by reference, so my first instinct would be to dictate that call-by-reference be used in all cases. I believed the opposite was true, however, because I wrongly thought that the designers of Ada valued safety over performance, which is not really true. > The question is, does someone who knows Ada get into trouble with > this rule? Clearly if you don't know a rule in the language you can > always get into trouble, you can't expect Ada to automatically > correct your misconceptions about the language. This is certainly a > lesson that you need to know a language well to avoid trouble, but > it is not convincing that there is a problem here. Of course one should know a language well, but in this case the language "wasn't very friendly". My point about C/C++ is that the programmer is forced to think about this issue immediately. All arrays are passed by reference, and all structs are passed by value - no ambiguity about it. You have to be aware of this to accomplish anything in the language. I learned of this problem within a month of programming in C, but I've been using Ada for a year, and this is the first time I've had this issue pop up. Maybe this reflects poorly on me, but what can I say! > Actually, I think most people who make a mistake here make a mistake the > other way round, they assume that arrays (and even records) have to be > passed by reference. Sure, don't doubt it. It makes perfect sense. > You actually remember the GNAT situation wrong, and if you remembered it > right, it would have reminded you of the issue. We changed record passing > from being by reference for large records to being always by value, as you > would like to see. This turned out to be completely unacceptable in some > situations because of severe degradation of performance -- so this is a > nice object lesson that your recommendation is infeasible! For the record, my recommendation is simply to clarify the issue. It would be nice if there were no implementation defined behavior, in a perfect computer world, but I'm not that naive. One possibility that hasn't been explored is this: define the rule as call-by-value *by default*, and give me a pragma or something to get the more efficient call-by-reference behavior. Then I would be forced to be aware of the pitfalls. It's not the greatest solution, but it least it gives the programmer more control. > P.P.S. In C++ you can't pass arrays anyway so the issue does not arise, so it > is wrong to say that the mechanism for this is defined in C++! If you cold > pass arrays in C or C++, then the same problem would arise. That's exactly my point, it is defined by not being possible! -- ------------------------------------------------------------------------------- Chris Felaco Phone: x4631 (Raynet 444, Local 842) Raytheon Company Email: bcf@ssd.ray.com ------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-22 0:00 ` Felaco @ 1996-07-22 0:00 ` Robert A Duff 1996-07-30 0:00 ` Richard A. O'Keefe 1996-07-22 0:00 ` Robert Dewar 1 sibling, 1 reply; 29+ messages in thread From: Robert A Duff @ 1996-07-22 0:00 UTC (permalink / raw) In article <wvyspakfnn4.fsf@swlpts1.ssd.ray.com>, Felaco <bcf@swlpts1.ssd.ray.com> wrote: >Of course I have to admit ignorance here. I never really thought >about it in Ada. For some reason I assumed that the compiler would in >some way ensure that this type of error would not occur, either by >passing the array by value, or reordering the assignments so the >problem would not occur. I realize I expected too much. You're not alone -- I've heard many professional Ada programmers who are surprised at the rule, or are surprised that there's a difference between by-copy and by-reference. That makes me nervous about this particular implementation dependence. >Well, I guess I never conceived a machine might exist where call-by- >value for anything bigger than a word is more efficient than call by >reference, Well, a word, or maybe 2 or 3 words -- it depends, for example, on how many machine registers are lying around. But that's basically right, call-by-value is more efficient for small-ish things. But machines have different word sizes. And it's not clear what fits in a word, even when you know the word size. For example, suppose I have a record containing 4 Character components (not packed). Some compilers will allocate 4 words, some will allocate 4 bytes -- it depends on the machine, and to some extent on the whim of the compiler writer. >... so my first instinct would be to dictate that >call-by-reference be used in all cases. All cases? Surely you don't mean *all*. Surely you want scalars passed by copy (in a register, normally). Maybe you meant "all array/record cases"? Well, that solves the implementation dependence problem, but it's less efficient. A packed array of 32 booleans fits in a word, and I would like it to be passed by copy, in a register. Also, Robert Dewar pointed out elsewhere the problem with passing components of packed objects (record-within-record, for example) by reference, when they're not byte-aligned. And slices. >... I believed the opposite was >true, however, because I wrongly thought that the designers of Ada >valued safety over performance, which is not really true. Right, the designers made trade-offs, and this is one that went in favor of efficiency over safety. (As I said elsewhere, I suspect it's possible to get both, but not simple.) >> The question is, does someone who knows Ada get into trouble with >> this rule? Clearly if you don't know a rule in the language you can >> always get into trouble, you can't expect Ada to automatically >> correct your misconceptions about the language. This is certainly a >> lesson that you need to know a language well to avoid trouble, but >> it is not convincing that there is a problem here. This is the standard argument that gets trotted out in favor of all manner of C pitfalls -- "the programmer should know the language". True, but unfortunately, many programmers do not know their language that deeply, and even ones who do make mistakes. I don't buy it in the C case, and I don't buy it in this particular Ada case. C has many things that are officially undefined, but programmers don't know that, or don't notice it in particular cases, and write non-portable and/or buggy code. Ada has just a few undefined things, so the problem is less severe, but it's still a problem. >Of course one should know a language well, but in this case the >language "wasn't very friendly". I agree. My answer is, "Yes it's unfriendly/unsafe, but it's an efficiency trade-off, and efficiency is important, too." >For the record, my recommendation is simply to clarify the issue. Well, the RM is pretty clear on this point, I think. >It would be nice if there were no implementation defined behavior, in >a perfect computer world, but I'm not that naive. I agree. It should be a language design goal to eliminate implementation dependence. However, it's not always feasible to do so. For example, how could you eliminate the implementation dependence inherent in delay statements? >One possibility that hasn't been explored is this: define the rule as >call-by-value *by default*, and give me a pragma or something to get >the more efficient call-by-reference behavior. Then I would be forced >to be aware of the pitfalls. It's not the greatest solution, but it >least it gives the programmer more control. Yes, that's one reasonable solution. It shouldn't be a pragma, but should use some sensible syntax. Some Pascal compilers do something like this -- there are by-value parameters, and CONST parameters (which are by-reference, but still in-mode), and VAR parameters (which are by-ref/in-out). Maybe CONST parameters are part of standard Pascal now; I don't know. Anyway, this solution has some problems (passing of components of packed things, forcing the programmer to specify something that *usually* doesn't matter, ..) The SPARK subset of Ada solves the problem by making sure you can't write code that can tell the difference between by-copy and by-ref. In particular, exception handling is not allowed, and aliasing of parameters is not allowed. This leads to a pretty restricted version of the language, though. - Bob ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-22 0:00 ` Robert A Duff @ 1996-07-30 0:00 ` Richard A. O'Keefe 0 siblings, 0 replies; 29+ messages in thread From: Richard A. O'Keefe @ 1996-07-30 0:00 UTC (permalink / raw) bobduff@world.std.com (Robert A Duff) writes: >Yes, that's one reasonable solution. It shouldn't be a pragma, but >should use some sensible syntax. Some Pascal compilers do something >like this -- there are by-value parameters, and CONST parameters (which >are by-reference, but still in-mode), and VAR parameters (which are >by-ref/in-out). Maybe CONST parameters are part of standard Pascal now; >I don't know. Anyway, this solution has some problems (passing of >components of packed things, forcing the programmer to specify something >that *usually* doesn't matter, ..) In ISO 10206 (ISO Pascal Extended) there are x: T; -- by value, local copy modifiable var x: T; -- by reference, original modifiable protected x: T; -- as if by value, local copy not modifiable protected var x: T; -- as if by reference, original not modifiable 'out' or 'in out' would have to be implemented as 'var', which specifies by-reference. 'in' corresponds to either protected value or protected var, both of which are more specific than in Ada. I am somewhat surprised that they didn't include protected pointers. -- Fifty years of programming language research, and we end up with C++ ??? Richard A. O'Keefe; http://www.cs.rmit.edu.au/~ok; RMIT Comp.Sci. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-22 0:00 ` Felaco 1996-07-22 0:00 ` Robert A Duff @ 1996-07-22 0:00 ` Robert Dewar 1 sibling, 0 replies; 29+ messages in thread From: Robert Dewar @ 1996-07-22 0:00 UTC (permalink / raw) Chris said "Well, I guess I never conceived a machine might exist where call-by- value for anything bigger than a word is more efficient than call by reference, so my first instinct would be to dictate that call-by-reference be used in all cases. I believed the opposite was true, however, because I wrongly thought that the designers of Ada valued safety over performance, which is not really true." Well as I hope is clear from my earlier post, your conception was not broad enough :-) Cases where values larger than one word are more efficiently passed by copy: - when bit alignment is involved (packed case) - several words can be passed in registers (according to many ABI's) - caller and callee are in different address spaces and here are undoubtedly more. Note that the idea of allowing implementation freedom here is not new. Fortran-66 is quite explicit in leaving it up to the implementation whether parameters are passed by value or reference. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 ` Robert Dewar 1996-07-21 0:00 ` Robert A Duff 1996-07-22 0:00 ` Felaco @ 1996-07-22 0:00 ` Karl Cooper {46901} 1996-07-22 0:00 ` Robert Dewar 1996-07-30 0:00 ` Felaco 3 siblings, 1 reply; 29+ messages in thread From: Karl Cooper {46901} @ 1996-07-22 0:00 UTC (permalink / raw) Robert Dewar wrote: > P.P.S. In C++ you can't pass arrays anyway so the issue does not arise, so it > is wrong to say that the mechanism for this is defined in C++! If you cold > pass arrays in C or C++, then the same problem would arise. This is really a C or C++ issue, but I still am not sure I understand. I seem to remember passing arrays as function parameters in both C and C++. Since an array reference is always implemented with a pointer in C and C++, all such passing is pass by reference. Why then do you say you can't pass arrays anyway? ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-22 0:00 ` Karl Cooper {46901} @ 1996-07-22 0:00 ` Robert Dewar 0 siblings, 0 replies; 29+ messages in thread From: Robert Dewar @ 1996-07-22 0:00 UTC (permalink / raw) Karl asks "This is really a C or C++ issue, but I still am not sure I understand. I seem to remember passing arrays as function parameters in both C and C++. Since an array reference is always implemented with a pointer in C and C++, all such passing is pass by reference. Why then do you say you can't pass arrays anyway?" You can pass a pointer *by value* in either C or Ada or C++, but this is NOT a call by reference. You can pass a *pointer* to an array, but not an array itself in C, but in Ada you can pass either a pointer to an array (which obviously has reference semantics at the array abstraction level, even though it is a call by value), OR you can pass an array. It is when an array is passed, something you cannot do in C, that the problem arises. Why not just go to the C model of passing pointers around? you might ask! The trouble with this approach is it requires far more aliasing than is comfortable in a language of this class. \ ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 ` Robert Dewar ` (2 preceding siblings ...) 1996-07-22 0:00 ` Karl Cooper {46901} @ 1996-07-30 0:00 ` Felaco 1996-07-31 0:00 ` Robert A Duff ` (2 more replies) 3 siblings, 3 replies; 29+ messages in thread From: Felaco @ 1996-07-30 0:00 UTC (permalink / raw) bobduff@world.std.com (Robert A Duff) writes: > Felaco wrote: > >One possibility that hasn't been explored is this: define the rule as > >call-by-value *by default*, and give me a pragma or something to get > >the more efficient call-by-reference behavior. Then I would be forced > >to be aware of the pitfalls. It's not the greatest solution, but it > >least it gives the programmer more control. > > Yes, that's one reasonable solution. It shouldn't be a pragma, but > should use some sensible syntax. Some Pascal compilers do something > like this -- there are by-value parameters, and CONST parameters (which > are by-reference, but still in-mode), and VAR parameters (which are > by-ref/in-out). Maybe CONST parameters are part of standard Pascal now; > I don't know. Anyway, this solution has some problems (passing of > components of packed things, forcing the programmer to specify something > that *usually* doesn't matter, ..) I disagree with changing the syntax. I feel that the pragma is the least intrusive way of giving the programmer some control, but not forcing the programmer to care when they wouldn't otherwise. Another even simpler idea is to dictate that the compiler issue a warning when a call is made to a subunit with the same actual parameter used as an 'in' and an 'out' (or 'in out'). How hard would this be? > The SPARK subset of Ada solves the problem by making sure you can't > write code that can tell the difference between by-copy and by-ref. In > particular, exception handling is not allowed, and aliasing of > parameters is not allowed. This leads to a pretty restricted version of > the language, though. I keep seeing SPARK mentioned in this thread. Although I don't think I like the sounds of it, where can I get more info about this? (Feel free to send direct e-mail rather than post a response.) dewar@cs.nyu.edu (Robert Dewar) writes: > If you want to think more about this issue, worry about packed arrays > too. Requiring call by reference would mean that ALL packed arrays have > to be passed using general bit pointers, which would be unacceptably > inefficient in the normal case where slices are not passed. Same > thing for records, all records would have to be passed by bit address, > just in case the record you are passing is a field in a rcord with a > record rep clause. That's a good question - just how does the compiler determine how to pass array parameters where rep specs or packing applies? Does it use bit pointers, or does it just make a properly aligned copy and pass it along? If the LRM dictated that all arrays were passed by reference, then the compiler could decide to use bit pointers (if the architecture provided efficient means to do so) or it could just resort to making a copy and passing a reference to it. In the latter case, the programming pitfall I am trying to avoid is impossible, but the programmer would still know to program defensively because the rule says arrays are passed by reference. This is not great, but slightly better. I still think there is some merit to my suggestion, but I won't defend it beyond that. It just seems strange to me that the language designers went to such lengths to restrict the usage of access types, but they failed to address this type of programming error which I feel is even more insidious. -- ------------------------------------------------------------------------------- Chris Felaco Phone: x4631 (Raynet 444, Local 842) Raytheon Company Email: bcf@ssd.ray.com ------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-30 0:00 ` Felaco @ 1996-07-31 0:00 ` Robert A Duff 1996-08-02 0:00 ` Robert Dewar 1996-08-03 0:00 ` JP Thornley 2 siblings, 0 replies; 29+ messages in thread From: Robert A Duff @ 1996-07-31 0:00 UTC (permalink / raw) In article <wvyafwh4pq7.fsf@swlpts1.ssd.ray.com>, Felaco <bcf@swlpts1.ssd.ray.com> wrote: >I disagree with changing the syntax. I feel that the pragma is the >least intrusive way of giving the programmer some control, but not >forcing the programmer to care when they wouldn't otherwise. Another >even simpler idea is to dictate that the compiler issue a warning when >a call is made to a subunit with the same actual parameter used as an ^^^^^^^ subprogram, I assume you mean >'in' and an 'out' (or 'in out'). How hard would this be? Impossible, since in many cases you can't tell what a given name denotes until run time (array indexing, pointer dereferencing). You could give overly pessimistic warnings, but that wouldn't be helpful, since you would have to warn for "P(X.all, Y.all);" if X and Y are of the same type, so you would get zillions of spurious warnings. Also, it's not just passing the same actual twice -- the problem can also occur if you pass overlapping things. (E.g., pass X and X(I).) Also, you need to worry about aliasing with globals, and the compiler can't see which globals are referenced. So it would have to warn about "P(X.all);", where X is a pointer, in case P accesses Y.all, and X and Y happen to point to the same thing. Also, Ada doesn't say aliasing is always wrong -- it depends on what the subprogram does, and in what order. >That's a good question - just how does the compiler determine how to >pass array parameters where rep specs or packing applies? Does it use >bit pointers, or does it just make a properly aligned copy and pass it >along? The latter. Bit pointers are less efficient than addresses on almost all machines. And if you pass a packed component by bit pointer, you have to pass *all* parameters that way (since the subprogram's code has to expect a bit pointer). A nasty distributed overhead. Alternatively, you could generate two versions of code for every subprogram, one of which expects addresses, and the other expects bit pointers. This isn't really acceptable, since it would double your code size. You could do better if you do fancy stuff at link time, but most compilers don't. So, compilers generate code that expects an address of a properly aligned thing, and in the packed case, make a copy at the call site that is properly aligned. This is what you expect -- pragma Pack means, "I want to shrink the size, and I'm willing to put up with slower access." >... If the LRM dictated that all arrays were passed by reference, >then the compiler could decide to use bit pointers (if the >architecture provided efficient means to do so) or it could just >resort to making a copy and passing a reference to it. This doesn't make sense to me. If the RM dictated by-reference, but the compiler used by-copy, then the compiler would simply be wrong. Passing a copy by reference is by-copy semantics. It doesn't matter who makes the copy (caller or callee) nor where it is allocated, nor how it is passed -- a copy is a copy. No, I think if you dictated by-ref, then you would have to introduce the Pascal rule, which says you can't pass packed components by-ref. This would be ugly, since it makes pragma Pack affect the semantics, rather than just giving an optimization hint. (Actually, pragma Pack *does* affect the semantics of shared variables in a tasking context. I find that ugly, too.) Note that Ada 95 has *some* types that require pass-by-ref (limited records, for example). If you pack something containing such a type, then the compiler simply doesn't pack that component tightly. - Bob ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-30 0:00 ` Felaco 1996-07-31 0:00 ` Robert A Duff @ 1996-08-02 0:00 ` Robert Dewar 1996-08-03 0:00 ` JP Thornley 2 siblings, 0 replies; 29+ messages in thread From: Robert Dewar @ 1996-08-02 0:00 UTC (permalink / raw) Felaco said: "That's a good question - just how does the compiler determine how to pass array parameters where rep specs or packing applies? Does it use bit pointers, or does it just make a properly aligned copy and pass it along? If the LRM dictated that all arrays were passed by reference, then the compiler could decide to use bit pointers (if the architecture provided efficient means to do so) or it could just resort to making a copy and passing a reference to it." That's a common confusion, but it is quite wrong. Call by reference is a semantic concept, it is not about passing pointers (the actual mechanism might well involve pointer passing, but that's an implementation level issue). Passing a pointer to a copy is NOT call by reference, but rather call by value. As for the first question, "Does it use bit pointers or does it just ..." the answer to this question is yes! ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-30 0:00 ` Felaco 1996-07-31 0:00 ` Robert A Duff 1996-08-02 0:00 ` Robert Dewar @ 1996-08-03 0:00 ` JP Thornley 1996-08-05 0:00 ` Roderick Chapman 2 siblings, 1 reply; 29+ messages in thread From: JP Thornley @ 1996-08-03 0:00 UTC (permalink / raw) Felaco <bcf@swlpts1.ssd.ray.com> writes: > I keep seeing SPARK mentioned in this thread. Although I don't think > I like the sounds of it, where can I get more info about this? (Feel > free to send direct e-mail rather than post a response.) Well, no-one from Praxis has posted a response to this (although maybe someone e-mailed) and ISTR there was an earlier post in this thread asking about it, so here's a short description. The SPARK language consists of:- 1. A subset of the Ada (83) language judged to be appropriate for producing safety-critical code. It can (rather crudely) be seen as the Pascal core of Ada, with some of the good Ada features added (packages, private types and some others). 2. A number of obligatory annotations (written as Ada comments) that describe the data flow in each subprogram (the derivation relationships between the imports and the exports). The SPARK Examiner is a tool that checks for conformance to the subset and that the Ada code correctly implements the data flows in the annotations. Other features of the SPARK toolset (which I haven't used yet) are the ability to generate path functions for a subprogram (so that the V&V people can check them against the specification) and a run time error checker that either (1) proves that a piece of code cannot cause a run-time error or (2) produces proof obligations (on the caller of that code) which (if met) ensure that a run-time error cannot be caused. HTH Phil Thornley -- ------------------------------------------------------------------------ | JP Thornley EMail jpt@diphi.demon.co.uk | ------------------------------------------------------------------------ ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-08-03 0:00 ` JP Thornley @ 1996-08-05 0:00 ` Roderick Chapman 0 siblings, 0 replies; 29+ messages in thread From: Roderick Chapman @ 1996-08-05 0:00 UTC (permalink / raw) JP Thornley wrote: > Well, no-one from Praxis has posted a response to this (although maybe > someone e-mailed) and ISTR there was an earlier post in this thread > asking about it, so here's a short description. > Information on SPARK and the SPARK Examiner is at http://www.praxis.co.uk/technols/spark/spark.htm Rod Chapman, Praxis Critical Systems. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-20 0:00 Call by reference vs. call by value Christopher Felaco 1996-07-20 0:00 ` James A. Krzyzanowski 1996-07-20 0:00 ` Robert Dewar @ 1996-07-21 0:00 ` Robert A Duff 1996-07-21 0:00 ` Robert Dewar 2 siblings, 1 reply; 29+ messages in thread From: Robert A Duff @ 1996-07-21 0:00 UTC (permalink / raw) In article <31F10E50.726@egr.uri.edu>, Christopher Felaco <felaco@egr.uri.edu> wrote: >My reasons for posting this are twofold. The first reason is to verify >that this is correct, ... It is. >... and see if anyone else has been bitten by this >particular undefined behavior. Yes, they have. >... The second reason is to express some >disappointment. I agree. Making this implementation dependent is bad language design, IMHO. However, your suggested solution -- to always pass 'in' parameters by reference -- is unacceptable from an efficiency point of view. It means you would have to copy huge amounts of data around when passing arrays from one procedure to another to another to another. It seems to me that a language can be designed so as to avoid the problem you are complaining about, while still retaining the desirable efficiency. But it wouldn't be Ada. - Bob ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-21 0:00 ` Robert A Duff @ 1996-07-21 0:00 ` Robert Dewar 0 siblings, 0 replies; 29+ messages in thread From: Robert Dewar @ 1996-07-21 0:00 UTC (permalink / raw) Robert Duff said "However, your suggested solution -- to always pass 'in' parameters by reference -- is unacceptable from an efficiency point of view. It means you would have to copy huge amounts of data around when passing arrays from one procedure to another to another to another." OOPS, typo! he meant "allways pass 'in' parameters by copy" incidentally, Algol-68 requires that the equivalent of in parameters always be passed by copy (it really cannot be otherwise in the Algol-68 semantic framework). However the only really successful implementation of Algol-68 (Ian Currie's Algol-68R), completely ignored this and passed all arrays by reference anyway -- he never got ONE complaint from a user (although see the 1968 Munich proceedings to see him being denounced by the Algol-68 cognoscenti -- Barry Mailloux accused him of heresy, and reminded him that there was only one source of truth -- the Algol 68 report :-) ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value
@ 1996-07-25 0:00 Marin David Condic, 407.796.8997, M/S 731-93
1996-07-26 0:00 ` Peter Amey
0 siblings, 1 reply; 29+ messages in thread
From: Marin David Condic, 407.796.8997, M/S 731-93 @ 1996-07-25 0:00 UTC (permalink / raw)
Peter Amey <pna@ERLANG.PRAXIS.CO.UK> writes:
>This is another Ada feature well covered by the SPARK subset. The rules
>of SPARK (which are checked by the SPARK Examiner) prohibit all cases
>of aliasing where program meaning might be affected by the parameter
>passing mechanism used. A SPARK program has copy-in, copy-out semantics
>regardless of the compiler used to compile it.
>
While I understand the interest in making language mechanisms
secure and predictable, I sometimes wonder about things like this
reference/value debate. It seems like a solution desparately
seeking a problem.
My experience with software bugs (especially in Ada) is that the
overwhelming bulk of them are some version of the computer doing
what you told it to do, not what you meant for it to do. (The
implementer got the logic wrong) Most forms of semanitc induced
errors are the kinds of things caught by the compiler (assigning
reals to integers without explicit conversion, uninitialized
variables, etc.) Those few weird "corner-case" bugs that fall into
the category of "the LRM said it was 'implementation dependent'
and my implementation bit me on the butt" happen so infrequently
(although are annoyingly difficult to find!) as to not be worth
the speed penalties imposed by "absolutely provable secure"
implementations.
I'd favor passing parameters by reference (for speed) over copy
mechanisms because in the practical world of "the way programmers
*really* program", my experience is thus: 1) You almost never see
code (in the real world) where parameter passing mechanisms cause
the problems (id est, where compiler "A" would have got it right
and compiler "B" got it wrong) 2) Most (Ada) compilers are pretty
good at finding the bulk of the parameter-passing problems no
matter what the implementation mechanism is through forms of
static analysis. (They're also pretty smart about figuring out
what's the *optimal* way to pass the parameters, depending on data
types.)
I think we've learned by now that it doesn't help a language to be
syntactically/semantically "perfect" (curing all the "Language
Lawyer" problems) at the expense of being slow, cumbersome and
late to market.
MDC
Marin David Condic, Senior Computer Engineer ATT: 407.796.8997
M/S 731-96 Technet: 796.8997
Pratt & Whitney, GESP Fax: 407.796.4669
P.O. Box 109600 Internet: CONDICMA@PWFL.COM
West Palm Beach, FL 33410-9600 Internet: CONDIC@FLINET.COM
===============================================================================
"Government is not reason. It is not eloquence. It is a force.
Like fire, a dangerous servant and a fearful master."
-- George Washington
===============================================================================
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: Call by reference vs. call by value 1996-07-25 0:00 Marin David Condic, 407.796.8997, M/S 731-93 @ 1996-07-26 0:00 ` Peter Amey 0 siblings, 0 replies; 29+ messages in thread From: Peter Amey @ 1996-07-26 0:00 UTC (permalink / raw) On Thu, 25 Jul 1996, Marin David Condic, 407.796.8997, M/S 731-93 wrote: > Peter Amey <pna@ERLANG.PRAXIS.CO.UK> writes: > >This is another Ada feature well covered by the SPARK subset. The rules > >of SPARK (which are checked by the SPARK Examiner) prohibit all cases > >of aliasing where program meaning might be affected by the parameter > >passing mechanism used. A SPARK program has copy-in, copy-out semantics > >regardless of the compiler used to compile it. > > [snip] > I'd favor passing parameters by reference (for speed) over copy > mechanisms ... SPARK does not enforce copying it simply ensures that the program _behaves_ as if parameters were copied even if the compiler passes by reference. I agree with many of your comments, but for the most critical of systems not worrying about the obscure/rare error cases is not an option. Also, tightening the language to the point where we can reason properly about program meaning allows us to tackle the serious problem you identify of the getting the logic wrong. The kinds of formal verification we routinely practice on critical code are simply not feasible if the meaning of a source text in not fully defined. Peter ^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~1996-08-05 0:00 UTC | newest] Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 1996-07-20 0:00 Call by reference vs. call by value Christopher Felaco 1996-07-20 0:00 ` James A. Krzyzanowski 1996-07-20 0:00 ` Robert Dewar 1996-07-20 0:00 ` Robert Dewar 1996-07-21 0:00 ` Robert A Duff 1996-07-21 0:00 ` Robert Dewar 1996-07-22 0:00 ` Robert A Duff 1996-07-23 0:00 ` Peter Amey 1996-07-23 0:00 ` Robert Dewar 1996-07-24 0:00 ` Robert A Duff 1996-07-23 0:00 ` Robert A Duff 1996-07-27 0:00 ` Peter Morris 1996-07-28 0:00 ` Robert A Duff 1996-07-24 0:00 ` Richard A. O'Keefe 1996-07-22 0:00 ` Felaco 1996-07-22 0:00 ` Robert A Duff 1996-07-30 0:00 ` Richard A. O'Keefe 1996-07-22 0:00 ` Robert Dewar 1996-07-22 0:00 ` Karl Cooper {46901} 1996-07-22 0:00 ` Robert Dewar 1996-07-30 0:00 ` Felaco 1996-07-31 0:00 ` Robert A Duff 1996-08-02 0:00 ` Robert Dewar 1996-08-03 0:00 ` JP Thornley 1996-08-05 0:00 ` Roderick Chapman 1996-07-21 0:00 ` Robert A Duff 1996-07-21 0:00 ` Robert Dewar -- strict thread matches above, loose matches on Subject: below -- 1996-07-25 0:00 Marin David Condic, 407.796.8997, M/S 731-93 1996-07-26 0:00 ` Peter Amey
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox