CMUCL, dealing with a similar infinitely-dynamic problem in Lisp, had a similar approach but dealt with it on a block-of-code level instead of per-function. You could declare a block to be compiled together, and any calls between functions in the block would be treated as having a promise that you'd never redef them without recompiling the whole block, so they could be statically optimized with respect to each other.
Not sure if that's a better or worse approach overall. It probably depends on the structure of your program. One nice feature was that the same function could be static from some perspectives and not from others--- to functions in the same compilation block it was static, but functions from outside that block that called it treated it as non-static, and would immediately get the new version if, say, that whole block were recompiled. That let you make the core code optimized by sticking it in one big compilation block, while not changing the normal dynamic Lisp semantics of the block when viewed from the perspective of any outside function.