While that's true, and switching to a slower path solves many problems, consider this common Ruby pattern:
Dir.glob("some dir/*").each do |filename|
require filename
end
A lot of Ruby code messes with how code is loaded (heck, "require" in almost every Ruby install is not the built in "require" but one that has been dynamically replaced). Sometimes that is intended to implement static behavior: The expectation is that the code remains the same. It's just a way of avoiding having to maintain a list manually.
But other times it is used as a plugin mechanism, where the expectation is for the code to be provided by a user or other developer, and processing this statically at compile time would remove capabilities that the user would expect.
You can't automatically determine things like that, because it is down to developer intent and the code does not document the intent.
So the problem goes deeper than being about being able to revert to a slow path: To compile Ruby in a meaningful way you need to make some level of trade-off in what aspects of Ruby you decide to treat as "ahead of time" vs runtime, because there is no clear rule that says "now the program has been loaded; everything after this is runtime".
But other times it is used as a plugin mechanism, where the expectation is for the code to be provided by a user or other developer, and processing this statically at compile time would remove capabilities that the user would expect.
You can't automatically determine things like that, because it is down to developer intent and the code does not document the intent.
So the problem goes deeper than being about being able to revert to a slow path: To compile Ruby in a meaningful way you need to make some level of trade-off in what aspects of Ruby you decide to treat as "ahead of time" vs runtime, because there is no clear rule that says "now the program has been loaded; everything after this is runtime".