I think we should keep track of whether variables were defined at toplevel. Then, when lifting, we should check whether all variables involved (e.g. in a closure environment) were defined at toplevel. If so, it's ok to lift. At present we make the decision based on where the potentially-liftable value is, which is more restrictive (e.g. we'll never lift a lambda under another lambda if its environment is inconstant, even if that environment can be computed at toplevel). The existing algorithm for placing "let symbol" bindings shouldn't need much modification to deal with this new criterion.
I think we should keep track of whether variables were defined at toplevel. Then, when lifting, we should check whether all variables involved (e.g. in a closure environment) were defined at toplevel. If so, it's ok to lift. At present we make the decision based on where the potentially-liftable value is, which is more restrictive (e.g. we'll never lift a lambda under another lambda if its environment is inconstant, even if that environment can be computed at toplevel). The existing algorithm for placing "let symbol" bindings shouldn't need much modification to deal with this new criterion.