How special is the global variable _G?

Viewed 17425

Excerpt from Lua 5.3 manual:

_G

A global variable (not a function) that holds the global environment (see §2.2). Lua itself does not use this variable; changing its value does not affect any environment, nor vice versa.

Relevant part from §2.2

[…] every chunk is compiled in the scope of an external local variable named _ENV, so _ENV itself is never a free name in a chunk.

[…]

Any table used as the value of _ENV is called an environment.

Lua keeps a distinguished environment called the global environment. This value is kept at a special index in the C registry. In Lua, the global variable _G is initialized with this same value. (_G is never used internally.)

When Lua loads a chunk, the default value for its _ENV upvalue is the global environment. Therefore, by default, free names in Lua code refer to entries in the global environment

I understand that for every chunk loaded, since _ENV would be the first upvalue, it is pointed to the global environment table, pointed by _G by load.

> =_G, _ENV
table: 006d1bd8 table: 006d1bd8

confirms that both point to the same table. The manual states, rather reassures multiple times, that _ENV and _G are just regular names with no hidden meaning and that Lua itself doesn't use it internally. I tried this chunk below:

local a = { }
local b = a      -- since tables are objects, both refer to the same table object
print(a, b)      -- same address printed twice
a = { }          -- point one of them to a newly constructed table
print(a, b)      -- new, old table addresses printed

Now doing the same with _G and _ENV:

local g = _G          -- make an additional reference
print(g, _G, _ENV)    -- prints same address thrice
local p = print       -- backup print for later use
_ENV = { }            -- point _ENV to a new table/environment
p(g, _G, _ENV)        -- old, nil, new

table: 00ce1be0    table: 00ce1be0    table: 00ce1be0
table: 00ce1be0    nil                table: 00ce96e0

If _G is an ordinary global, why is it becoming nil here? If reference counting is done, _G, was still holding a reference at the time _ENV released it. Like b above, it too should be holding on to the old table, no?

However, for the below chunk, _G is unchanged / preserved!

_ENV = { _G = _G }
_G.print(_G, _ENV, _ENV._G)   -- old, new, old

But here it is killed:

_ENV = { g = _G }
_ENV.g.print(_ENV, _ENV.g, _G)    -- new, old, nil

Another case where it is preserved:

print(_G, _ENV)                       -- print same address twice
local newgt = {}                      -- create new environment
setmetatable(newgt, {__index = _G})   -- set metatable with _G as __index metamethod
_ENV = newgt                          -- point _ENV to newgt
print(_G, newgt, _ENV)                -- old, new, new

With so many variations in the behaviour of _G, the original reassurance given by the manual seems shaky. What am I missing here?

1 Answers
Related