Subject: change the value of 4

From: Chen Shapira <[email-masked]>
Date: Sun, 17 Feb 2002 12:12:24 +0200

Hi,

Someone once hinted to me that in Fortran once can change the value of 4. 
What did he mean? Do numbers in Fortran are like variable names? How exectly
do you do it? simply 4=5?
Anyone knows on other languages that support this mis-feature?

Thanks,
Chen.

---

From: "Nadav Har'El" <[email-masked]>
Date: Sun, 17 Feb 2002 12:36:40 +0200

> Anyone knows on other languages that support this mis-feature?

I see people are interested in Fortran war-stories :)

In fortran, parameters to functions are always passed by reference. When you
call F(A), F gets A by reference and is allowed to both read and write it.
This is obviously implemented (in the generated assembly code) by passing to
the called function a pointer to A, rather than the value of A.

Now, here comes the problem: What does fortran do when you call F(4)? It
cannot pass the literal "4", and must instead set up a memory space, put
a 4 in it, and give F the pointer to that memory space. One implementation
I checked (and it will be interesting to check if g77 also does this) chooses
not to reset that memory position to 4 on every call to F(4). So if F changes
its parameter to 5 (remember, it is allowed to do that), the next calls to
F(4) might actually give F a parameter 5! The compiler might (and I don't
remember checking that) even use the same memory location for several "4"s
mentioned in the program, so all 4s are changed into 5s :)

The horror!

I assume that all languages that don't have a way to "pass-by-value" arguments
to functions will potentially have this problem. But there are two ways to
avoid this problem in such languages:
  1. Allow saying that certain variables are "input" only (if I remember
     correctly, you can do this in Pascal and in Ada - but correct me if
     I'm wrong). The compiler will refuse to pass constants as non-input
     parameters.
  2. When passing a constant 4, allocate place on the stack, put a 4 in it,
     and give the called function a pointer to this stack position. Do this
     again and again every time the function is called. Obviously, this will
     have a (relatively small) performance effect, which is perhaps why some
     Fortran compilers didn't do it.

Well, at least you can't say "4=5" in fortran. Reminds me of the following
joke (http://www.netfunny.com/rhf/jokes/88q4/15881.html):

     The primary purpose of the DATA statement is to give names to
     constants; instead of referring to pi as 3.141592653589793 at every
     appearance, the variable PI can be given that value with a DATA
     statement and used instead of the longer form of the constant.  This
     also simplifies modifying the program, should the value of pi change.

		-- FORTRAN manual for Xerox Computers

-- 
Nadav Har'El                        |         Sunday, Feb 17 2002, 5 Adar 5762
[email-masked]             |-----------------------------------------
Phone: +972-53-245868, ICQ 13349191 |I have an open mind - it's just closed
http://nadav.harel.org.il           |for repairs.

---

From: "Nadav Har'El" <[email-masked]>
Date: Sun, 17 Feb 2002 13:37:05 +0200

I'm replying to myself :)

>      I'm wrong). The compiler will refuse to pass constants as non-input

The input/output qualifiers indeed exist in Ada. In Pascal you have a "var"
qualifier for seperating call by value and call by reference.

By the way, in Fortran 90, there's a new INTENT(IN), INTENT(OUT) and
INTENT(INOUT) qualifiers. Reminds me of another joke on how radically
different Fortran 90 is from Fortran 77, and still called Fortran:
See http://www.netfunny.com/rhf/jokes/88q4/11267.7.html
This guy is commenting to the Fortran 90 standartization comittee. The
punchline:
  "As I said in my comments to the committee, nice language, too bad
   it's not Fortran."

-- 
Nadav Har'El                        |         Sunday, Feb 17 2002, 5 Adar 5762
[email-masked]             |-----------------------------------------
Phone: +972-53-245868, ICQ 13349191 |I love deadlines. I love the whooshing
http://nadav.harel.org.il           |sound they make as they go by.

---

From: Adi Stav <[email-masked]>
Date: Sun, 17 Feb 2002 17:02:13 +0200

> mentioned in the program, so all 4s are changed into 5s :)

Not only in Fortran. In C, the equivalent would be modifying string 
literals, since C arrays can only passed as references, just like 
in Fortran. That would be undefined behaviour in ANSI and crash 
under GCC/Linux, but I suspect it was allowed in K&R1 -- if you 
pass -ftraditional (actually, -fwritable-strings is the "active 
ingredient") to GCC you'll be able to do the same trick. And if you 
give up strict compliance with either testament and just ask ld/GCC 
to mark the text section as writable ("-N"), modifying one string 
literal will affect the all the different string literals in the 
same file that just happen to have the same contents!

> The horror!

The above string literal trick was used to great effect in several 
IOCCC entries I had had the misfortune to glance upon. 

Now that I'm thinking about it, a function could probably also do 
the same with the /other/ object that can only be passed by 
reference, namely callback functions. All that it would take is a 
single memcpy() from a smaller function, the "-N" flag, and a 
certain inclination to evil.

---

From: "Nadav Har'El" <[email-masked]>
Date: Sun, 17 Feb 2002 14:44:32 +0200

> same file that just happen to have the same contents!

Right :)

There's another very disturbing peculiarity with C's string constants.
ANSI C added the "const" keyword exactly so you'll be able to tell the
compiler that a passed-by-reference (pointer, actually) parameter is not
to be changed. So you'd expect that a function taking "char *" (rather than
"const char *" will not agree to take a string constant, telling you some
'trying to promote constant into non-constant' or something of that sort.
Unfortunately, none of the ANSI C compilers I ever tried give you such
an error, and all happily compile things like

	void ruin(char *s){
		s[2]='h';
	}
	int main(void){
		ruin("a string constant");
	}

I guess this horrible relaxation of the "const" rules was done to allow
legacy programs (that don't use "const" anywhere) to compile.

-- 
Nadav Har'El                        |         Sunday, Feb 17 2002, 5 Adar 5762
[email-masked]             |-----------------------------------------
Phone: +972-53-245868, ICQ 13349191 |Take my advice, I don't use it anyway.
http://nadav.harel.org.il           |

---

From: mulix <[email-masked]>
Date: Sun, 17 Feb 2002 14:52:02 +0200 (IST)

> 'trying to promote constant into non-constant' or something of that sort.

nope. the type of "foo" is a char [sizoef("foo")]. promoting it to
const char* will probably break old programs. (although the current
behaviour will also break them, since you'll get a SIGSEGV in ruin()).

> legacy programs (that don't use "const" anywhere) to compile.

it's not a relaxation - the type of "a string constant" is char
[sizeof(string)].

did you check if gcc has a flag to control this (the type of "foo"), btw?
-- 
mulix

http://vipe.technion.ac.il/~mulix/
http://syscalltrack.sf.net/

---

From: "Nadav Har'El" <[email-masked]>
Date: Sun, 17 Feb 2002 15:04:40 +0200

> did you check if gcc has a flag to control this (the type of "foo"), btw?

Actually, gcc does have such a flag:

       -Wwrite-strings
              Give  string  constants the type const char[length]
              so that copying the address of one into a non-const
              char  * pointer will get a warning.  These warnings
              will help you find at compile time  code  that  can
              try  to  write  into a string constant, but only if
              you have been very careful  about  using  const  in
              declarations  and  prototypes.   Otherwise, it will
              just be a nuisance; this is why  we  did  not  make
              `-Wall' request these warnings.

But I was thinking that this should have been part of the standard, not
some obscure -W (warning) option in a specific compiler (gcc). I realize
this would break old programs and libraries - but let's admit it - ANSI C
has existed for so long (since 1992? I don't remember) that this would not
have been a problem if they simply had the guts to put it in the standard.

-- 
Nadav Har'El                        |         Sunday, Feb 17 2002, 5 Adar 5762
[email-masked]             |-----------------------------------------
Phone: +972-53-245868, ICQ 13349191 |You do not need a parachute to skydive.
http://nadav.harel.org.il           |You only need one to skydive twice.

---

From: Adi Stav <[email-masked]>
Date: Sun, 17 Feb 2002 18:36:48 +0200

> have been a problem if they simply had the guts to put it in the standard.

That would have required rewriting practically every C code that 
used strings, and would enforce the use of a feature that was 
meant to be optional (like prototypes), and which already 
controvertial. I doubt ANSI C could have had impact that it had
were that its attitude towards K&R compatility.

Besides, a whole lot of modern C code doesn't use const either, 
a perfectly acceptible practice. So making string literals 
"const char[]" would not be any easier now than it was in 1992.

---

From: Oleg Goldshmidt <[email-masked]>
Date: 17 Feb 2002 23:21:57 -0500

> mentioned in the program, so all 4s are changed into 5s :)

I can easily believe that there were implementations that did just that.
However, normally you'll segfault in any sane compiler, I believe.

G77 is not smart enough to warn you at compile time that you are
trying to do something illegal. The following code

      subroutine f(x)
      integer*4 x
      write(6,*) x
      x = 5
      end

      program four
      call f(4)
      call f(5)
      stop
      end

compiles cleanly with g77 and segfaults at "x = 5" at run time. This
is, of course, the same behaviour as in your "ruin" C example.

-- 
Oleg Goldshmidt | [email-masked] 
If it aint't broken it hasn't got enough features yet.