> > This is an insidious (and rather silly) bug, and as far as I can see it is
> > the only way how a global var can be masked out of existence (rather than
> > hidden by symbols with the same name that are closer in scope).
> It's exactly the same thing. Look at what happens in the debugger, and
> pause in the called routine and do a DISPLAY MEMORY from the command window.
Well, we know that Fox hides the global variable but this behaviour does not make sense at all, and it is
completely different from the hiding implied by normal scoping rules.
The fact that a symbol in an inner scope hides symbols
with the same name in outer scopes is just how scoped names are expected to work, it is just a result of the name having a different meaning within the inner scope (the name simply refers to a different entity, at least potentially). However, normal name scoping also implies that inner scopes can only add or redefine names, but it is not possible to remove names - which is exactly what the bug does.
Furthermore, most coding standards proscribe special names for the few globals that are permitted to exist (like prefixing such names with "g_"), and so it is simply not possible for local names to hide global names in code written to conform with such a standard.
The consequence of the bug is that special consideration/coding is required whenever globals must be passed by reference, like using an intermediary local variable or documenting why the temporary 'non-existence' of the global var is not expected to cause problems with the called code (which presumes complete knowledge of the called code, including any calls to other subsystems that this code may make, and which creates nice Heisenbug potential for maintenance).
Here's some repro code that demonstrates how this can byte you even if you are only a little bit guilty:
release g_uTest
public g_uTest
sele sele("orders")
use home(2)+"\Northwind\orders" noup agai orde customerid
brow last nowa
sele sele("customers")
use home(2)+"\Northwind\customers" noup agai
set rela to rela_(customerid) into orders in customers
brow last nowa
b0rken_(@m.g_uTest)
set rela off into orders in customers
rele g_uTest
function b0rken_ (ruParam)
? type([m.g_uTest]), program()
set date dmy
function rela_ (uExpr)
? type([m.g_uTest]), program()
return m.uExpr
The output:
L RELA_
U B0RKEN_
U RELA_
U RELA_