[net.lang] Constants in subroutine calls

ken@rochester.UUCP (Ken Yap) (01/06/85)

The problem is not using constants in subroutine calls per se but
changing a parameter that corresponds to a constant.

BTW I saw a neat way of trapping this sort of error without any
run-time overhead in the VMS Fortran compiler. Constants are placed in
a read-only segment and the VM system traps any violation. True, you
may get an obscure error message if your run-time doesn't understand
what happened but it is better than carrying on innocently.

Can anybody tell me if there are other compilers out there that also do
this? If not, is there any reason this is not more widely practiced?
(Other than lack of memory management hardware, that is.)
-- 
	Ken Yap

UUCP: (..!{allegra, decvax, seismo}!rochester!ken) ARPA: ken@rochester.arpa
USnail:	Dept. of Comp. Sci., U. of Rochester, NY 14627. Voice: Ken!

dgary@ecsvax.UUCP (D Gary Grady) (01/07/85)

<>
The FORTRAN code

CALL FOO(A, 5, B)

or some such calling a routine like this:

SUBROUTINE FOO(X, Y, Z)
..
Y = 1
..
RETURN
END

results in the constant 5 being changed to 1 in the rest of the program.
The original poster had resorted to never using a constant in a
subroutine call to get around this bug.  But the fact is that it IS a
compiler bug which existed in a number of early FORTRAN compilers but
which I thought had long since been removed.

An actual parameter (argument) in a CALL statement can be a variable, a
subprogram name, a statement number (preceded by an asterisk), or an
expression.  A numerical constant is a degenerate case of an expression.
All expressions should be dealt with by copying the result into a work
area and passing a pointer to that.  This has the nice side effect that
a variable can be passed on a call-by-value basis simply by enclosing it
in parentheses.

I suspect the bad behavior of allowing constants to be altered by
subroutine calls may stem from a cute trick in the compiler's lexical
analyser.  Constants can be inserted into the symbol table as
self-defining "initialised variables."  If this is done, statements such
as 1 = 2 will work!  This would no doubt bring much amusement to the
denizens of net.math, but *I* am not amused.  (That's ~:-) in standard
notation.)

Oh, well...

-- 

D Gary Grady
Duke University Computation Center, Durham, NC  27706
(919) 684-4146
USENET:  {decvax,ihnp4,akgua,etc.}!mcnc!ecsvax!dgary

joe@petsd.UUCP (Joe Orost) (01/08/85)

In article <5143@rochester.UUCP> ken@rochester.UUCP (Ken Yap) writes:
>The problem is not using constants in subroutine calls per se but
>changing a parameter that corresponds to a constant.
>
>BTW I saw a neat way of trapping this sort of error without any
>run-time overhead in the VMS Fortran compiler. Constants are placed in
>a read-only segment and the VM system traps any violation. True, you
>may get an obscure error message if your run-time doesn't understand
>what happened but it is better than carrying on innocently.
>
>Can anybody tell me if there are other compilers out there that also do
>this? If not, is there any reason this is not more widely practiced?
>(Other than lack of memory management hardware, that is.)

I don't know how they got away with that.  Consider the following:

	CALL A(1.0)

	SUBROUTINE A(R)
	CALL B(R)
	END

	SUBROUTINE B(R)
	...
	END

Since scalar arguments are passed by value, the value 1.0 is copied into "R"
on entry to subroutine A.  On exit from A, the compiler can't know if B
changed the value of R (it also doesn't know that the argument is a
constant), so it must copy back the local copy of R into the constant 1.0.
This prevents us from storing the constant in read-only memory.  It seems
that the only way thay can get away with storing passed constants in read-only
memory is to CHECK what was passed on subroutine exit, and skip the update
code if a constant was passed (yick!).

According to the ANSI '77 Fortran standard 15.9.2 (15):

"Actual arguments may be constants, symbolic names of constants, function
references, expressions involving operators, and expressions enclosed in
parenthesis IF AND ONLY IF THE ASSOCIATED DUMMY ARGUMENT IS A VARIABLE THAT
IS NOT DEFINED DURING THE EXECUTION OF THE REFERENCED EXTERNAL PROCEDURE."

This means that it is erroneous to pass a constant to a routine which
changes its dummy argument.

In our compiler, we allow a routine to change a dummy argument that is
associated with an expression without side effects, but changing a constant
or a symbolic constant is taboo.

					regards,
					joe

--
Full-Name:  Joseph M. Orost
UUCP:       ..!{decvax,ucbvax,ihnp4}!vax135!petsd!joe
US Mail:    MS 313; Perkin-Elmer; 106 Apple St; Tinton Falls, NJ 07724
Phone:      (201) 870-5844
Location:   40 19'49" N / 74 04'37" W

davidg@dartvax.UUCP (David Gelhar) (01/08/85)

> BTW I saw a neat way of trapping this sort of error without any
> run-time overhead in the VMS Fortran compiler. Constants are placed in
> a read-only segment and the VM system traps any violation. 
 
The DTSS PL/1 compiler also knows how to keep constants in a read-only
segment to allow them to be passed by reference. At the moment though,
this is done only in a special compiler mode used for compiling the
operating system; it is stylistically unpleasant to assign to a parameter
that was passed a constant, but the language semantics allow it.
For the operating system, the efficiency gained by passing write-protected
constants by reference instead of making a copy was judged to be worth the
price of altering normal PL/1 semantics a bit, but this is not done by
default, lest novice users be baffled.

ech@spuxll.UUCP (Ned Horvath) (01/09/85)

This awful FORTRAN behavior is wired into the STANDARDS, to wit: FORTRAN uses
call by 'value-result': each argument is copied, the copies are passed, and
at return the copies are copied back to the original arguments.  This is not
quite the same as call by reference, since (for example) you can pass a
variable which is in COMMON and the subroutine can modify either copy without
the effects being replicated in the other -- until it returns!

Unfortunately, many compiler writers simply implemented call by reference.
You can test your compiler with the above example: pass a COMMON variable,
modify the parameter, and see if the COMMON variable reflects the change
WHILE STILL IN THE SUBROUTINE.  If so, you have call by reference.

Note that either call by reference or call by value-result will clobber your
constants.  The trick recently suggested -- pass (1.0) rather than 1.0 --
is a nice way to defeat this in FORTRAN, works fine in PL/I (call by
reference) also.

Beware, however: your 'optimizer' might just be braindamaged enough to strip
off the parens!

=Ned=

stew@harvard.ARPA (Stew Rubenstein) (01/10/85)

Joe Orost writes:

>  Consider the following:
> 
> 	CALL A(1.0)
> 
> 	SUBROUTINE A(R)
> 	CALL B(R)
> 	END
> 
> 	SUBROUTINE B(R)
> 	...
> 	END
> 
> Since scalar arguments are passed by value, the value 1.0 is copied into "R"
> on entry to subroutine A.  On exit from A, the compiler can't know if B
> changed the value of R (it also doesn't know that the argument is a
> constant), so it must copy back the local copy of R into the constant 1.0.
> This prevents us from storing the constant in read-only memory.  It seems
> that the only way thay can get away with storing passed constants in
> read-only memory is to CHECK what was passed on subroutine exit, and skip the
> update code if a constant was passed (yick!).

In VMS Fortran, everything is passed by reference.  Thus, in subroutine A,
it doesn't copy 1.0 into "R", it copies the ADDRESS of the constant into
the argument list to be passed to subroutine "B".  No copying of the value
is necessary.  If subroutine "B" tries to alter the contents of this address,
it will generate an access violation.

Stew
-- 
-----------------------
Stew Rubenstein     UUCP: ihnp4!harvard!stew
Harvard Chemistry   ARPA: stew@harvard

jlg@lanl.ARPA (01/10/85)

> In VMS Fortran, everything is passed by reference.  Thus, in subroutine A,
> it doesn't copy 1.0 into "R", it copies the ADDRESS of the constant into
> the argument list to be passed to subroutine "B".  No copying of the value
> is necessary.  If subroutine "B" tries to alter the contents of this address,
> it will generate an access violation.


A lot of Fortran compilers do that.  The CRAY compiler makes a copy of the
constant and passes the address of the copy down to the subroutines.  That
way, if one of the subroutines changes the constant's value, only the copy
is changed.  The CRAY doesn't have 'read-only' memory locks, so it can't
detect errors for this case (this may be the intended semanitcs anyway, see
example below).


      SUBROUTINE A(R)         !PRINT THE PRODUCT OF R AND (R+1), RETURN R+1
      TEMP=R
      CALL SUCC(R)
      PRINT *,TEMP*R
      RETURN
      END

      SUBROUTINE SUCC(R)      !RETURN SUCCESSOR OF R
      R=R+1
      RETURN
      END

Subroutine A will work correctly on the CRAY, even when its argument is a
constant.  The 'error' doesn't propagate back up to the calling routine,
since only a copy of the constant was altered.  (This is an admittedly
contrived example, but it does demonstrate the point.  I have used this
technique to advantage before - it eliminates the need to use dummy variables
to pass constant values to subroutines when I don't care about the returned
value.  I always carefully flag this trick as being non-standard.)

The CRAY compiler is standard conforming too, since the standard doesn't
require non-conforming code to be reported or corrected.  The standard only
requires that conforming programs be executed correctly.


                                              James Giles

dgary@ecsvax.UUCP (D Gary Grady) (01/11/85)

<>
Warning: pathetic apology follows; overly sentimental readers may want
to skip...

WRONG!!  I was WRONG!!!!!  Mea culpa!

I had claimed that existing FORTRAN compilers allow a constant to be
passed as argument to a subprogram without that constant being damaged
if it is modified within the subprogram.  The FORTRAN compiler I used
years ago behaved nicely in that regard.  I have now discovered (by
trying it out) that IBM's current VS FORTRAN compiler DOES allow you to
make 1=2 and other horrors.

I continue to consider that silly behavior.  The runtime overhead of
copying constants into a work area is trivial.  But the fact remains
that I was dead wrong, and wrong due for the simple reason that I fired
off a response because I was "sure" of something before I tested it.  In
the future I will attempt to do better.

I await suggestions for pennance.
-- 
D Gary Grady
Duke U Comp Center, Durham, NC  27706
(919) 684-3695
USENET:  {seismo,decvax,ihnp4,akgua,etc.}!mcnc!ecsvax!dgary

jlg@lanl.ARPA (01/11/85)

> This awful FORTRAN behavior is wired into the STANDARDS, to wit: FORTRAN uses
> call by 'value-result': each argument is copied, the copies are passed, and
> at return the copies are copied back to the original arguments.  This is not
> quite the same as call by reference, since (for example) you can pass a
> variable which is in COMMON and the subroutine can modify either copy without
> the effects being replicated in the other -- until it returns!

I just reread the standard, and that's NOT what it says. 'Value-result' is
not required or implied by the standard (a standard conforming compiler is
allowed to use 'value-result' if it remains semantically true to the
standard's requirements).  I've never worked with a Fortran compiler which
used 'value-result' parameter passing (I have used several non-Fortran
languages with this parameter passing method).

According to the standard, you CAN'T pass a variable in both common and as
a dummy argument (ANSI X3.9-1978 sec. 15.9.3.6).  I don't know of any way
for the compiler to check for violations of this constraint, but it is non-
standard.

> Unfortunately, many compiler writers simply implemented call by reference.
> You can test your compiler with the above example: pass a COMMON variable,
> modify the parameter, and see if the COMMON variable reflects the change
> WHILE STILL IN THE SUBROUTINE.  If so, you have call by reference.

Most Fortrans use call by reference for ALL parameters.  It's not
unfortunate, it's just the way things are.  I've never found it to be a
hardship.

> Note that either call by reference or call by value-result will clobber your
> constants.  The trick recently suggested -- pass (1.0) rather than 1.0 --
> is a nice way to defeat this in FORTRAN, works fine in PL/I (call by
> reference) also.

A good Fortran compiler will pass a copy of the constant, not the original
in read-only memory.  For example, The CRAY Fortran compiler makes a copy
of all constant arguments into the temporary data area and passes these
copies to the subroutine (the exception is that character constants are
passed directly (I think - will have to check)).

I think that the constraint against passing constants to subroutines which
change argument values (ANSI X3.9-1978 sec. 15.9.2) should be removed.  I
like to pass constants to such routines when I don't care what the returned
value is going to be, and the technique of passing copies allows this to
work just fine.

                                                James Giles

donn@utah-gr.UUCP (Donn Seeley) (01/12/85)

	From: ech@spuxll.UUCP (Ned Horvath)

	This awful FORTRAN behavior is wired into the STANDARDS, to
	wit: FORTRAN uses call by 'value-result': each argument is
	copied, the copies are passed, and at return the copies are
	copied back to the original arguments.  This is not quite the
	same as call by reference, since (for example) you can pass a
	variable which is in COMMON and the subroutine can modify
	either copy without the effects being replicated in the other
	-- until it returns!

ANSI X3.9-1978, section 15.9.3.6, p. 15-20:

	If a subprogram reference causes a dummy argument to become
	associated with an entity in a common block in the referenced
	subprogram or in a subprogram referenced by the referenced
	subprogram, neither the dummy argument nor the entity in the
	common block may become defined within the subprogram or within
	a subprogram referenced by the referenced subprogram.  For
	example, if a subroutine contains the statements:

		SUBROUTINE XYZ (A)
		COMMON C

	and is referenced by a program unit that contains the
	statements:

		COMMON B
		CALL XYZ (B)

	then the dummy argument A becomes associated with the actual
	argument B, which is associated with C, which is in a common
	block.  Neither A nor C may become defined during execution of
	the subroutine XYZ or by any procedures referenced by XYZ.

As has been said before in this newsgroup, the standard does not
require either call by reference or call by value-result -- it permits
either.

The equine mammal appears to have expired despite repeated concussions,

Donn Seeley    University of Utah CS Dept    donn@utah-cs.arpa
40 46' 6"N 111 50' 34"W    (801) 581-5668    decvax!utah-cs!donn

ndiamond@watdaisy.UUCP (Norman Diamond) (01/13/85)

>       SUBROUTINE A(R)         !PRINT THE PRODUCT OF R AND (R+1), RETURN R+1
>       TEMP=R
>       CALL SUCC(R)
>       PRINT *,TEMP*R
>       RETURN
>       END
> 
>       SUBROUTINE SUCC(R)      !RETURN SUCCESSOR OF R
>       R=R+1
>       RETURN
>       END
> 
> Subroutine A will work correctly on the CRAY, even when its argument is a
> constant.  The 'error' doesn't propagate back up to the calling routine,
> since only a copy of the constant was altered.  (This is an admittedly
> contrived example, but it does demonstrate the point.  I have used this
> technique to advantage before - it eliminates the need to use dummy variables
> to pass constant values to subroutines when I don't care about the returned
> value.  I always carefully flag this trick as being non-standard.)
> 
> The CRAY compiler is standard conforming too, since the standard doesn't
> require non-conforming code to be reported or corrected.  The standard only
> requires that conforming programs be executed correctly.
> 
> 
>                                               James Giles

Yes, the CRAY compiler conforms to the standard.  Your program does not conform
to the standard.

A compiler can do whatever it wishes in cases that do not have to be reported
or corrected.  A compiler can implement dozens of extensions, and technically
just be required to report when the extensions are used.  A program that uses
such extensions is a non-conforming program, and no one should compilain when
it doesn't port very easily.

About subroutine A working "correctly", Mr. Giles meant that subrouting A does
what he wants it to do.  Lots of other possibilities would also be correct, as
far as the standard is concerned.

-- Norman Diamond

UUCP:  {decvax|utzoo|ihnp4|allegra|clyde}!watmath!watdaisy!ndiamond
CSNET: ndiamond%watdaisy@waterloo.csnet
ARPA:  ndiamond%watdaisy%waterloo.csnet@csnet-relay.arpa

"Opinions are those of the keyboard, and do not reflect on me or higher-ups."