[comp.lang.c] structure function returns -- how?

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.