Since Lua 5.2 non-locals are loaded from the table/object _ENV. Every lexical context has either an implicit _ENV, or an explicit _ENV (e.g. nearest `local _ENV = ...`). The default value for implicit _ENV is the global table (i.e. _G, but not literally _G; it's initialized as if from lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD)). See https://www.lua.org/manual/5.4/manual.html#2.2
Lua 5.1 had something similar, but the implicit environment was per-function.
A common pattern in Lua is to change _ENV to an empty table with a __newindex meta-method that writes-through to the real global environment, and an __index meta-method that throws an error if the key (i.e. global symbol) doesn't exist. You can fancy and have an __index meta-method that that throws an error string with suggestions for a possibly misspelled symbol, similar to clang or GCC compiler diagnostics.
Lua 5.1 had something similar, but the implicit environment was per-function.
A common pattern in Lua is to change _ENV to an empty table with a __newindex meta-method that writes-through to the real global environment, and an __index meta-method that throws an error if the key (i.e. global symbol) doesn't exist. You can fancy and have an __index meta-method that that throws an error string with suggestions for a possibly misspelled symbol, similar to clang or GCC compiler diagnostics.