notes@hcx1.UUCP (12/05/86)
Suppose a is declared as a structure and b is a function which returns a structure. In the statement: a = b () ; when and how should the copying into a take place? The pcc compilers I have seen apparently "set" b in b's stack frame. They then return a pointer to b in that frame, and copying from that pointer is done in a's frame. But what happens if a signal comes through during the copy? The contents of b , which are not yet copied, are overwritten by the signal routine's stack frame? Another compiler overcomes this problem by passing the address of a local structure in a's frame to the function b . Thus, instead of "setting" b , a's local structure is filled up, and copying from that local structure occurs when b returns. Are there other issues to be considered? Dave Ray -- uucp: {ucf-cs|allegra}!novavax!hcx1!daver
campbell@maynard.BSW.COM (Larry Campbell) (12/07/86)
In article <131@hcx1.UUCP> notes@hcx1.UUCP writes: >Suppose a is declared as a structure and b is a function which >returns a structure. In the statement: > a = b () ; >when and how should the copying into a take place? The compiler on my system (which is pcc based) allocates static unnamed storage in b. It puts the result there and returns a pointer to the storage. The routine containing the assignment then copies the result into a, using the pointer returned by b. It seems to me that this is also signal-unsafe -- if a signal occurs just before the copy, and the signal handler calls b -- whammo. -- Larry Campbell The Boston Software Works, Inc. Internet: campbell@maynard.bsw.com 120 Fulton Street, Boston MA 02109 uucp: {alliant,wjh12}!maynard!campbell +1 617 367 6846 ARPA: campbell%maynard.uucp@harvisr.harvard.edu MCI: LCAMPBELL
chris@mimsy.UUCP (Chris Torek) (12/08/86)
In article <131@hcx1.UUCP> notes@hcx1.UUCP (Dave Ray) writes: >The pcc compilers I have seen apparently "set" [a return value] in >[the staack frame of the function that returns a structure]. >They then return a pointer to [this stack object], and copying from >that pointer is done [by the caller]. But what happens if a signal >comes through during the copy? The return value gets clobbered. The 4BSD Vax PCC allocates a static return value in a function that returns a structure, and returns a pointer to the static return value. I.e., given struct foo f() { ... return (rv); } g() { struct foo t; t = f(); } the compiler in fact generates code more like this: struct foo * f() { static struct foo _temp_; ... _temp_ = rv; return (&_temp_); } g() { struct foo t; t = *f(); } >Another compiler overcomes this problem by passing the address of a >local structure in [the caller]'s frame to the [structure valued] function. That works too. >Are there other issues to be considered? PCC's method is not re-entrant, but averts disaster when people call a structure-valued function just for side effects, and neglect to declare the function first. If I were writing the compiler, I would use the last method: the caller should pass a pointer to an object to be set to the return value. This is in particular more efficient in the f() and g() example above, since g() can pass a pointer to `t' and avoid a copy. One possible problem with this is in code of this form: struct foo f() { ... } g() { static struct foo t; t = f(); } Since `t' is static here, it is conceivable that there might be problems with non-atomic adjustment of `t' in an optimised version of f(), if f() calls g() recursively. To give a more concrete example (vertically compressed for small screens): typedef struct point { int x, y; } point; point f() { struct point rv; rv.y = global_y++; rv.x = more() ? g() : 0; return (rv); } int g() { static struct point last_point = { 1, 1 }; if (more()) return (last_point.y + 1); last_point = f(); return (last_point.y); } An `optimising' compiler might compile instead this: void f(rvp) point *rvp; { rvp->y = global_y++; rvp->x = more() ? g() : 0; } int g() { static struct point last_point = { 1, 1 }; if (more()) return (last_point.y + 1); f(&last_point); return (last_point.y); } But this will not always have the same effect, since f() could alter g's last_point during, rather than after, the recursion, making g() return a different value in the recursive call. A `correct' optimisation of f(): void f(rvp) point *rvp; { int _temp_y = global_y++; rvp->x = more() ? g() : 0; rvp->y = _temp_y; } That is, all the assignments must be done at the end of the function in the presence of recursion. Asynchronous calls (signals) are even worse, but create atomicity problems with any scheme. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu
henry@utzoo.UUCP (Henry Spencer) (12/13/86)
>Suppose a is declared as a structure and b is a function which >returns a structure. In the statement: > a = b () ; >when and how should the copying into a take place? It's an awkward problem, since struct values generally don't fit in the registers that are used to return ordinary values. The best solution is for the caller to allocate space for the returned value and communicate the address to the callee somehow, so the callee can copy the value there before returning. This does require that the caller know the returned type, and there is a lot of sloppiness about this in C, especially when the returned value is not being used. (Although said sloppiness may be less common for struct-valued functions.) There can also sometimes be difficulties in implementing it. There are various alternatives, some of which indeed are not signal-proof. Using a static area to return the value leads to trouble in the (quite uncommon) case of struct-returning functions being used in signal handlers, but has the virtue of being easy to retrofit into old compilers. -- Henry Spencer @ U of Toronto Zoology {allegra,ihnp4,decvax,pyramid}!utzoo!henry
firth@sei.cmu.edu (Robert Firth) (12/17/86)
In article <7403@utzoo.UUCP> henry@utzoo.UUCP (Henry Spencer) writes: >>Suppose a is declared as a structure and b is a function which >>returns a structure. In the statement: >> a = b () ; >>when and how should the copying into a take place? > >It's an awkward problem, since struct values generally don't fit in the >registers that are used to return ordinary values. The best solution is >for the caller to allocate space for the returned value and communicate >the address to the callee somehow, so the callee can copy the value there >before returning. This does require that the caller know the returned >type, and there is a lot of sloppiness about this in C, especially when >the returned value is not being used. (Although said sloppiness may be >less common for struct-valued functions.) There can also sometimes be >difficulties in implementing it. There are various alternatives, some >of which indeed are not signal-proof. Using a static area to return the >value leads to trouble in the (quite uncommon) case of struct-returning >functions being used in signal handlers, but has the virtue of being easy >to retrofit into old compilers. >-- > Henry Spencer @ U of Toronto Zoology > {allegra,ihnp4,decvax,pyramid}!utzoo!henry This problem occurs also in other languages, and it's a shame to see people reinvent solutions over and over. Here, then, are all the solutions I know of, with comments. (a) Registers. If the structured object is small enough, use the registers anyway. A two-word struct, for instance, can surely be returned in <r0,r1> or your machine's equivalent. (b) Caller preallocates. This passes as an extra parameter a pointer to a result area computed by the caller. Note that this requires the caller to know the size of the result, which is always the case in C but not the case in, eg, Ada. There is a subtle trap here if we have code like static structthing s ... s = f() the temptation is to pass &s as the pointer, which causes strange results if f() has another access path to s. That's called 'aliasing'. Caller should allocate a local temporary unless really sure it is safe to pass a pointer to a declared variable. If the ultimate caller does the right thing then f() can pass that pointer down to an inner call of a similar function g(), as has been mentioned. (c) Function allocates static space and returns pointer. Caller copies. This is not reentrant, as has been pointed out. Nor does it work if the function is recursive. Since reentrancy bugs are very hard to find, as a compiler writer I would absolutely NEVER use this technique. A variation is for the function to return a pointer to a declared object holding the value. For example, if the function does a lookup of an external array of struct objects, it can simply return a pointer into the table. (d) Function allocates heap space, rest as above. The technique used by several Algol-68 implementations. It requires a true heap (with garbage collection) in Algol-68. In C you can have the function allocate and the caller always free after doing the copy. This always works but can be rather expensive. (e) Function leaves result on stack. This is usually easiest for the function. The problem is that on many systems an interrupt or signal will destroy the result. (flame)(This is a symptom of a major and persistent system design error: the use of a hardware register pointing into user space as a place to dump junk. It is compounded by language implementations that use the hardware stack to allocate the LIFO address space of local variables). There are ways around this 1. Protect the user stack. You can do this in bsd 4.3, for example, by arranging for signals to use a separate stack 2. Ignore the hardware stack and allocate local variables somewhere else. This is done by, for instance, BCPL on the PDP-11 and VAX-11. 3. Have a magic routine "return_result". This takes as its parameter the result (and, probably, its size). It is called as the last act of the function. It returns to the caller's caller, doing the right things to the stack, and always keeping the stack pointer below any valid data. If the size of the result is always known at compile time, I'd pick method (b). Overall it's simplest and not too expensive. Otherwise, I'd use e2, e1, or e3 (in diminishing order of preference)
henry@utzoo.UUCP (Henry Spencer) (12/19/86)
> (c) Function allocates static space and returns pointer. Caller copies. > > This is not reentrant, as has been pointed out. Nor does it work if > the function is recursive... Not true, given proper code generation. The static area need be in use only during the return sequence, hence recursion is no problem -- execution of multiple instances of a recursive function overlaps, but the return sequences need not. Static-area return values may even speed up recursive returns, since less copying may be needed. Reentrancy is another story, although it should be noted that reentrancy is another story for a *whole* *lot* of things in a C/Unix environment: a signal handler has to be very careful, since locking out signal handlers during critical sections in (say) stdio is expensive and hence seldom done. >(e) Function leaves result on stack. > > This is usually easiest for the function. The problem is that on many > systems an interrupt or signal will destroy the result. (flame)(This is a > symptom of a major and persistent system design error: the use of a > hardware register pointing into user space as a place to dump junk. It > is compounded by language implementations that use the hardware stack > to allocate the LIFO address space of local variables)... No, it's a symptom of a major and persistent software error: using a stack pointer as if it were an index register. If you get it firmly into your head that you are dealing with a *stack*, not just an index register that happens to increment and decrement automatically in some circumstances, then the problem goes away. When you say "leaves result on stack", what you really mean is "leaves result beyond the end of the stack, assuming that the stack will not be used as a stack until the result is picked up". It is possible, although often awkward, to leave the result *on the stack*, i.e. as if it had been pushed onto the stack, in which case there isn't a problem with interrupts or signals. (This is approximately what your e3 alternative does, but in an unnecessarily roundabout way using a magic run-time routine.) > (d) Function allocates heap space, rest as above. > > ...always works but can be rather expensive. It always works in the absence of reentrancy, or if your memory allocator is reentrant, which many aren't. Additional alternatives that you have missed are some variations on (b) (caller preallocates): (1) it isn't necessary for the address to be passed as a parameter if the caller and callee can establish mutual agreement on where the result goes, although this is generally practical only if the stack is contiguous; (2) it may be possible to use the argument area as the return area, if the caller ensures it is big enough and avoids popping it off the stack or re-using it before picking up the result, and the callee makes sure that result copying comes after all uses of arguments (especially in the case where the returned value is the value of an argument). [Incidentally, it reflects badly on you to chide us for re-inventing the wheel when you seem not to be aware of some of the prior art yourself. You might find Bell Labs Computing Science Tech Report No. 102, "The C Language Calling Sequence", by Johnson and Ritchie, Sept. 1981, to be interesting reading.] -- Henry Spencer @ U of Toronto Zoology {allegra,ihnp4,decvax,pyramid}!utzoo!henry
firth@sei.cmu.edu (Robert Firth) (12/22/86)
In article <7434@utzoo.UUCP> henry@utzoo.UUCP (Henry Spencer) writes: >> (c) Function allocates static space and returns pointer. Caller copies. >> >> This is not reentrant, as has been pointed out. Nor does it work if >> the function is recursive... > >Not true, given proper code generation. The static area need be in use >only during the return sequence, hence recursion is no problem -- execution >of multiple instances of a recursive function overlaps, but the return >sequences need not. Static-area return values may even speed up recursive >returns, since less copying may be needed. Reentrancy is another story, >although it should be noted that reentrancy is another story for a *whole* >*lot* of things in a C/Unix environment: a signal handler has to be very >careful, since locking out signal handlers during critical sections in (say) >stdio is expensive and hence seldom done. > Agreed, but the codegeneration had better be very careful. The trouble comes if you are returning a composite value, and the computation of one or more components involves a function call. You can't use the return area to build up the value component by component. >>(e) Function leaves result on stack. >> >> This is usually easiest for the function. The problem is that on many >> systems an interrupt or signal will destroy the result. (flame)(This is a >> symptom of a major and persistent system design error: the use of a >> hardware register pointing into user space as a place to dump junk. It >> is compounded by language implementations that use the hardware stack >> to allocate the LIFO address space of local variables)... > >No, it's a symptom of a major and persistent software error: using a stack >pointer as if it were an index register. If you get it firmly into your >head that you are dealing with a *stack*, not just an index register that >happens to increment and decrement automatically in some circumstances, >then the problem goes away. When you say "leaves result on stack", what >you really mean is "leaves result beyond the end of the stack, assuming that >the stack will not be used as a stack until the result is picked up". It >is possible, although often awkward, to leave the result *on the stack*, >i.e. as if it had been pushed onto the stack, in which case there isn't a >problem with interrupts or signals. (This is approximately what your e3 >alternative does, but in an unnecessarily roundabout way using a magic >run-time routine.) Alternatively, it's bad hardware design adding special semantics to one of the index registers... Let's agree that hardware stacks are a mess and agree to differ on the cause of the mess. Pushing the result on the hardware stack and then taking the return can be very hard indeed, since the Return instruction usually pops the link off the stack. So you have to copy maybe the entire call frame below your pushed result. It most cases that's a lot of code, and you probably want it out of line. >> (d) Function allocates heap space, rest as above. >> >> ...always works but can be rather expensive. > >It always works in the absence of reentrancy, or if your memory allocator >is reentrant, which many aren't. Yes, Henry, thank you for the caveat. I confess I had simply forgotten that people design languages in which preemptive parallelism is possible and then don't make the libraries properly reentrant. >Additional alternatives that you have missed are some variations on (b) >(caller preallocates): (1) it isn't necessary for the address to be passed >as a parameter if the caller and callee can establish mutual agreement >on where the result goes, although this is generally practical only if >the stack is contiguous; (2) it may be possible to use the argument area >as the return area, if the caller ensures it is big enough and avoids >popping it off the stack or re-using it before picking up the result, >and the callee makes sure that result copying comes after all uses of >arguments (especially in the case where the returned value is the value >of an argument). Agreed. I was giving only general methods that should work on most machines, but failed to make that clear. These are all possible tricks it's useful to know. Your (1) saves a parameter (and may simplify the stack handling on the caller's side); I'm dubious whether the space saved by (2) is worth the burden it places on the called function. Just to repeat a point, I still think the key factor in the design is whether the caller ALWAYS knows the size of the returned value. If it does, you have a several easy solutions; if it doesn't, you have a harder task. And this does affect implementors of C, if they want to use the same codegenerator for C and for languages with dynamically-sized return values. >[Incidentally, it reflects badly on you to chide us for re-inventing the >wheel when you seem not to be aware of some of the prior art yourself. >You might find Bell Labs Computing Science Tech Report No. 102, "The C >Language Calling Sequence", by Johnson and Ritchie, Sept. 1981, to be >interesting reading.] >-- > Henry Spencer @ U of Toronto Zoology > {allegra,ihnp4,decvax,pyramid}!utzoo!henry Well, what I said was "It's a shame to see people reinvent solutions". If that gave the impression of chiding someone, I apologise; the comment was meant purely as an introduction to what was supposed to be a technically helpful post. Incidentally, why do you think I haven't read a work just because I don't quote it? Now if only C had full type checking of parameter lists!
stuart@bms-at.UUCP (Stuart D. Gathman) (12/24/86)
In article <490@aw.sei.cmu.edu.sei.cmu.edu>, firth@sei.cmu.edu (Robert Firth) writes: [ we are discussing returning structures from functions ] > (a) Registers. If the structured object is small enough, use the Definitely do this is all cases. > (b) Caller preallocates. This passes as an extra parameter a pointer > to a result area computed by the caller. Note that this requires In most cases this will be object being assigned to. Only when the selection operator is applied to the function result will a temp area have to be allocated. See ultimate solution below. > (c) Function allocates static space and returns pointer. Caller copies. I hate this because it is inefficient; the structure is copied twice! Unfortunately all of the compilers we have do it because it is simple for the compiler. However, some of the objections below do not hold water. > This is not reentrant, as has been pointed out. Nor does it work if > the function is recursive. Since reentrancy bugs are very hard to > find, as a compiler writer I would absolutely NEVER use this technique. It *is* recursive (the static area is only used by the return statement - think about it). It is also reentrant if each process has its own static area as it should anyway for anything reasonable to happen. (This is where the const storage class comes in handy. Constants can be shared. Shared data has to be accessed via pointers or else declared via a non-ansi global storage class.) This method has problems when the function is called from a signal catching function. > A variation is for the function to return a pointer to a declared > object holding the value. For example, if the function does a lookup > of an external array of struct objects, it can simply return a pointer > into the table. In fact, it can return a pointer to any class of variable including auto - except that signals will bomb when auto is handled in this way. It is still recursive and reentrant though and eliminates the double copy. This works because the function result is an rvalue and can be assigned or passed on the stack without wiping its value on most architectures. > (d) Allocate on heap Boooooo! Hisss. You might as well not allow functions returning structures; it is that inefficient. > (e) Function leaves result on stack. This works best when done a la pascal. The caller decrements the stack pointer (for descending stacks) to reserve room for the result before stacking function arguments. The function does the copy. Another copy is required if doing an assignment, but passing the result to another function requires no extra overhead! (Not even the code in the calling sequence to do the copy.) Knowledge of the structure size is required at compile time. This is re* and immune to interrupts. It also works with signals. *** The real solution *** (f) For an assignment, the caller passes a pointer to the object being assigned. The function does the copy. For a function argument (including selection), the stack pointer is decremented as in (e) and a pointer to the stack area so reserved is passed. (I.e. save the stack pointer in a register, decrement the stack pointer, push the register.) Again, the function does the copy. This is recursive, reentrant (even without seperate static areas), immune to interrupts, works with signals, the result is always copied only once, and the code for the copy is in the function - not the calling sequence! (I hate it when the result is copied twice! Grrr.) P.S. This idea is free provided that the user implements it on our compilers or else hires me to implement it on their compiler. :-) -- Stuart D. Gathman <..!seismo!dgis!bms-at!stuart>
tim@ism780c.UUCP (Tim Smith) (12/30/86)
If one makes the caller, rather than the callee, do the unlink after a call, then one can return stuff on the stack with no problem with recursion or re-entrance. -- Tim Smith USENET: sdcrdcf!ism780c!tim Compuserve: 72257,3706 Delphi or GEnie: mnementh
tim@ism780c.UUCP (Tim Smith) (12/30/86)
If the called function does the copying, then the following can cause problems: struct SpanishInquisition nobody, expected(); nobody = expected(); expected(); How does expected() tell these two cases apart? If it doesn't, then it is going to blow away something when it tries to copy back a structure in the second call. I suppose the compiler could note that the return value is being ignored, and pass a pointer to some temporary place to hold the ignored return value, but that seems kind of ugly. -- Tim Smith USENET: sdcrdcf!ism780c!tim Compuserve: 72257,3706 Delphi or GEnie: mnementh
meissner@dg_rtp.UUCP (Michael Meissner) (01/01/87)
In article <5076@ism780c.UUCP> tim@ism780c.UUCP (Tim Smith) writes: > >If the called function does the copying, then the following can >cause problems: > struct SpanishInquisition nobody, expected(); > > nobody = expected(); > expected(); > >How does expected() tell these two cases apart? If it doesn't, then >it is going to blow away something when it tries to copy back a structure >in the second call. > >I suppose the compiler could note that the return value is being ignored, >and pass a pointer to some temporary place to hold the ignored return >value, but that seems kind of ugly. If the compiler returns structures by having the caller pass a pointer to an area that is copied into, then for the second example, the compiler does have to create a temporary (preferably a stack temp...) and pass it's pointer. If it doesn't, the compiler has an undocumented feature (ie, bug) and it should be fixed. BTW, the Data General compiler does return structures in this manner, and yes we do pass the address of a temporary in an accumulator. -- Michael Meissner, Data General ...mcnc!rti-sel!dg_rtp!meissner
chris@mimsy.UUCP (Chris Torek) (01/02/87)
In article <782@dg_rtp.UUCP> meissner@dg_rtp.UUCP (Michael Meissner) writes: >If the compiler returns structures by having the caller pass a pointer to >an area that is copied into, then for the second example [in which the >return value is ignored], the compiler does have to create a temporary >(preferably a stack temp...) and pass it's pointer. Alternatively, the compiler can pass a nil pointer, and generate code to recognise such in structure-valued functions. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu
firth@sei.cmu.edu (Robert Firth) (01/05/87)
In article <326@bms-at.UUCP> stuart@bms-at.UUCP (Stuart D. Gathman) writes: >*** The real solution *** > >(f) For an assignment, the caller passes a pointer to the object > being assigned. The function does the copy. > >P.S. This idea is free provided that the user implements it >on our compilers or else hires me to implement it on their compiler. :-) >-- >Stuart D. Gathman <..!seismo!dgis!bms-at!stuart> Sorry. This fails if the called function accesses the destination object via another path. Consider for instance Z1 : COMPLEX; Z1 := SWAP_RE_AND_IM(Z1); with parameter and result object implemented by reference. The code of the SWAP has return COMPLEX'(ARG.IM,ARG.RE); If the result is assigned piecemeal to the result place, then Z1.RE will have been overwritten. This is clearly a compiler bug, since nearly all languages specify that the RHS shall be completely evaluated BEFORE the assignment is performed, and the error arises because the implementation violates those semantics.
firth@sei.cmu.edu (Robert Firth) (01/05/87)
In article <5075@ism780c.UUCP> tim@ism780c.UUCP (Tim Smith) writes: > >If one makes the caller, rather than the callee, do the unlink >after a call, then one can return stuff on the stack with no problem >with recursion or re-entrance. >-- >Tim Smith USENET: sdcrdcf!ism780c!tim Compuserve: 72257,3706 > Delphi or GEnie: mnementh Absolutely right, Tim! Which is one (of many) reasons why the compiler writer should be free to choose how to call, link, return, unlink &c. Which is one (of many) reasons why hardware instructions such as VAX CALLS, that preempt your choice, are undesirable.
levy@ttrdc.UUCP (Daniel R. Levy) (01/12/87)
In article <505@aw.sei.cmu.edu.sei.cmu.edu>, firth@sei.cmu.edu.UUCP writes: >In article <326@bms-at.UUCP> stuart@bms-at.UUCP (Stuart D. Gathman) writes: >>*** The real solution *** >>(f) For an assignment, the caller passes a pointer to the object >> being assigned. The function does the copy. >>Stuart D. Gathman <..!seismo!dgis!bms-at!stuart> >Sorry. This fails if the called function accesses the destination >object via another path. Consider for instance >Z1 : COMPLEX; >Z1 := SWAP_RE_AND_IM(Z1); >with parameter and result object implemented by reference. >The code of the SWAP has > return COMPLEX'(ARG.IM,ARG.RE); >If the result is assigned piecemeal to the result place, >then Z1.RE will have been overwritten. This is clearly a >compiler bug, since nearly all languages specify that the >RHS shall be completely evaluated BEFORE the assignment is >performed, and the error arises because the implementation >violates those semantics. This "failure" can only happen if the arguments are passed by reference, as well as the destination. If they are passed by value, or if the ap- parent passing of struct arguments by value is really implemented by passing them by reference but the called routine (with struct-valued return value) is "smart" enough to use copies in cases of overlap instead of the originals, you avoid this problem. -- ------------------------------- Disclaimer: The views contained herein are | dan levy | my own and are not at all those of my em- | an engihacker @ | ployer or the administrator of any computer | at&t computer systems division | upon which I may hack. | skokie, illinois | -------------------------------- Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa, allegra,ulysses,vax135}!ttrdc!levy
firth@sei.cmu.edu (Robert Firth) (01/12/87)
In article <1438@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes: >In article <505@aw.sei.cmu.edu.sei.cmu.edu>, firth@sei.cmu.edu.UUCP writes: >>In article <326@bms-at.UUCP> stuart@bms-at.UUCP (Stuart D. Gathman) writes: >>>*** The real solution *** >>>(f) For an assignment, the caller passes a pointer to the object >>> being assigned. The function does the copy. >>>Stuart D. Gathman <..!seismo!dgis!bms-at!stuart> >>Sorry. This fails if the called function accesses the destination >>object via another path. Consider for instance >>Z1 : COMPLEX; >>Z1 := SWAP_RE_AND_IM(Z1); >>with parameter and result object implemented by reference. [*] >>The code of the SWAP has >> return COMPLEX'(ARG.IM,ARG.RE); >>If the result is assigned piecemeal to the result place, >>then Z1.RE will have been overwritten. This is clearly a >>compiler bug, since nearly all languages specify that the >>RHS shall be completely evaluated BEFORE the assignment is >>performed, and the error arises because the implementation >>violates those semantics. > >This "failure" can only happen if the arguments are passed by reference, >as well as the destination. If they are passed by value, or if the ap- >parent passing of struct arguments by value is really implemented by passing >them by reference but the called routine (with struct-valued return value) >is "smart" enough to use copies in cases of overlap instead of the originals, >you avoid this problem. And I said that[*]. I also said 'for instance', in the naive belief that one example would be enough of a hint to show why the general method is wrong. So here is another 'for instance': the called function accesses the result object directly, and not via a by-reference parameter. Same erroneous behaviour, and the only fix is NOT to pass a pointer to the result object. Nor is this aliasing; the function has no logical visibility of its result object. Apologies to the rest of you for beating this thing to death.