I don't think so; within the same block of statements, all variables are automatically bound to the same environment (i.e. a table), and the compiler does not need to know if it's local or external: all of them are local and accessed by the "__index" meta entry of the environment table, which is always used as first level of indirection before performing an actual lookup to the environment table itself (not its metatable).
Unlike tables in Lua, all environments must have a metatable associated to their table, so there's always an "__index" entry in it (it also has a "__newindex" for assignments). A compiler may want to perform some optimizations for not creating a metatable with "__index" and "__newindex", but it cannot safely know if these two entries are set or not (they may be set by the block of instruction by using the fsetenv function, possibly by calling external functions which will execute with the parent element in their on environment linked to the parent environment, and so can also modify the parent environment).
So all names are local. The fact that when assigning a variable or reading it has an external effect comes only from the fact that the default "__index" function will lookup in parent environments in a chain to see if there's a matching name: if no such name is found in the chain, then the effect of reading the variable will return "nil"; the same occurs for "__newindex" which also tries to lookup the local table, then if not found performs a lookup in the parent environment, and if not found it will then create a new variable in the initial environment.
All you want is to stop the recursive lookup of variable names in the chain of environment, so that all variables behave as pure local variables (creating as many new variables as needed).
It's not really possible to block the recursion: your code even needs the chain for all basic operations (including operators like "+"). If you break the lookup, then your local code can simply do nothing at all!
Remember that the environment does not include only local variables, it also includes all functions and operators your code can use.
So your proppsed "blind" keyword in:
function (_ENV,c,s) blind
return {x=c*x-s*y,y=s*x+c*y}
end
would have the effect of leaving only three names accessibles: _ENV, c and s, but operations like "=" (assignment made via "__newindex" function call), "*", "-", and "+" would also have no defined function (their lookup would return nil, and you'd then get errors: cannot call a function referenced by nil !
The only way to do that is to allow passing selected properties you need for your function to run, by creating a restrictive environment, in which the function:
function (c,s)
return {x=c*x-s*y,y=s*x+c*y}
end
now can run in perfect isolation: it is effectively the case that variable names "x" and "y" are not defined locally, but you have to force them to use the local environment and not any parent environment, but you sill need the function references for the 3 arithmetic operators. Note that for function calls (including operator evaluations) there's also a "__call" entry in the environment to find matching function names: functions are not called directly.
An interesting reading:
or more generally
and the manual of course (which details all "__" prefixed functions needed in valid environment and that allow your code to be really executable) :