TCO (tail call optimization) is often confused with TCE (tail call elimination), the latter is a runtime guarantee whereas the former is a compiler's best effort attempt to statically optimize tail calls.
I am trying to think of a situation where a functional language compiler does not have enough information at compile time, especially when effects are witnessed by types.