1. Local functions which have enclosing environments are harder to inline. If a function's environment is different to the caller's environment, we should replace all its free variables to references to its environment. For simplicity, you can inline only the combinators(functions which have no free variables).
2. When inlining, we should rewrite the parameters only if they are free to the function body, not bound by other local functions in the body.
1. I'm not proposing yet to inline local functions, especially those that close on environments. However, what algorithm would you propose for inlining local functions?
As an aside, closure-conversion makes the creation of environments explicit. Perhaps an inlining step can be added after closure-conversion?
I see. This is actually handled implicitly in the compiler's AST structure: during the conversion from the list form to the AST form, each local variable is given a unique ID:
(fn (x y) (g x y (fn (x) (h x))))
=>
(fn (x@1 y@2) (g x@1 y@2 (fn (x@3) (h x@3))))
; approximation of the AST structure, the AST
; is really a table of properties
So mindless replacement of the inlined version will simply replace x@1, not x@3.
Hmm. Turns out this is a real issue, but for a different reason: since local variables are given unique ID's, we should actually replace local variable ID's for subfunctions when a function is inlined several times: