There is a change of behaviour somewhere between 1.3x and 2.1x.
I recall in my previous job when I had real code to look after, that if the change was in the arg count of a 'private' function then you could simply recompile the .42m and deploy it to the customer site without needing to relink and send all the affected .42r's
Try this (just checked it still works in 1.33.2e)...
-- 0305a.4gl
MAIN
CALL a1(1)
CALL b1(1)
END MAIN
FUNCTION a1(x)
DEFINE x INTEGER
DISPLAY "a1"
END FUNCTION
--0305b.4gl
FUNCTION b1(x)
DEFINE x INTEGER
DISPLAY "b1"
CALL b2(x,x)
END FUNCTION
-- Private function
FUNCTION b2(x,y)
DEFINE x,y INTEGER
DISPLAY "b2"
END FUNCTION
fgllink -o 0305.42r 0305a.42m 0305b.42m
fglrun 0305
I could then make the following change to the 'private' function in 0305b.4gl ( added a 3rd argument)
--0305b.4gl
FUNCTION b1(x)
DEFINE x INTEGER
DISPLAY "b1"
CALL b2(x,x,x)
END FUNCTION
-- Private function
FUNCTION b2(x,y,z)
DEFINE x,y,z INTEGER
DISPLAY "b2"
END FUNCTION
fglcomp 0305b.4gl
fglrun 0305
and it would run fine without the need for relinking. My interpretation is that the runner first looked for the function inside the current .42m and if it found it, it then didn't need to look in the .42r for it.
From an ISV point of view this behaviour was quite desirable we could if needed make a change/bug-fix to the internals of a library/common routine and then deploy the affected .42m. I don't recall us doing it that often (our deployment procedures never really caught onto the fact you could deploy a single .42m, I mean how do you communicate to the customer how to test it, especially when you tell them it could effect every single program) but as long as the function signature of the 'public' functions didn't change it was available.
This was part of the reasoning behind my suggestions at the time of PRIVATE FUNCTION, and an ability to test if the function signature of 'public' functions had changed. In this example there was no need for the 'private' function b2 to appear in the .42r