On 11/9/2015 9:37 PM, Steven D'Aprano wrote:
The compiler doesn't need to decide *in advance* whether the attribute might have changed. It knows whether it has changed or not *at runtime*.
You are using 'compiler' when you should, to avoid confusion, use 'interpreter'.
It's one thing to say that *in principle* any function might modify or shadow builtins. That's true, because we don't know what's inside the function. But the compiler knows, because it actually executes the code
'interpreter'
inside the function and can see what happens when it does. It doesn't have to predict in advance whether or not calling `func(x)` shadows the builtin `len` function, *it can see for itself* whether it did or not. At compile time, `func(x)` might do anything. But at runtime, we know exactly what it did, because it just did it. Imagine that the compiler keeps track of whether or not builtins has been
/compiler/code compiled by the compiler and interpreted by the interpreter (which could be the CPU)/
modified. Think of it as a simple "dirty" flag that says "yes, builtins is still pristine" or "no, something may have shadowed or modified the builtins". That's fairly straight-forward: builtins is a dict, and the compiler can tell whether or not __setitem__ etc has been called on that dict. Likewise, it can keep track of whether or not a global has been created that shadows builtins: some of that can be done statically, at compile-time, but most of it needs to be done dynamically, at runtime.
This is more or less Victor Stinner's proposal, and he has a working prototype that runs nearly all the test suite. He now plans to refine it and measure changes in space and time usage.
If the flag is set, the compiler knows that the optimization is unsafe and it has to follow the standard name lookup, and you lose nothing: the standard Python semantics are still followed. But if the flag is clear, the compiler knows that nothing has shadowed or modified builtins, and a whole class of optimizations are safe. It can replace a call to (say) len(x) with a fast jump, avoiding an unnecessary name lookup in globals, and another unnecessary name lookup in builtins. Or it might even inline the call to len. Since *most* code doesn't play tricks with builtins, the overhead of tracking these changes pays off *most* of the time -- and when it doesn't, the penalty is very small.
-- Terry Jan Reedy -- https://mail.python.org/mailman/listinfo/python-list