Zauważ że TCO można wyłączyć, a przede wszystkim optymalizacje są domyślnie wyłączone w trybie Debug. Czyli podczas debugowania nie musisz się przejmować tym że TCO Ci zepsuje stack trace, bo po prostu go nie ma.
Pytanie nie brzmiało "po co?" tylko "czy coś takiego jest?".
Naprawdę nie interesuje mnie czy wg was ma to sens, pytam tylko czy coś takiego jest. A wprost odpowiedzi do tej pory nie uzyskałem.
No dobrze, proszę. Nie ma. Wystarczająco wprost, myślę.
Mając wiedzę o asemblerze, wiesz prawdopodobnie że stacktrace powstaje w pewnym sensie 'niejawnie', jako efekt uboczny normalnego działania programu.
Żeby stacktrace działało przy TCO, kompilator musiałby niejawnie tworzyć gdzieś jakąś listę 'ominiętych' ramek stosu i dołączać ją do stacktrace (czyli przy każdym wywołaniu ogonowym zapisujemy gdzieś informację że ono nastąpiło, żeby je później odczytać tworząc stacktrace).
Ponieważ ta lista ominiętych ramek nie może być na stosie (wtedy efektywnie TCO zmieniłoby się efektywnie w WTF), trzeba też pamiętać żeby ją gdzieś trzymać w synchronizacji z tym co się faktycznie dzieje.
Czyli jeśli foo
woła ogonowo bar
zapisujemy gdzieś że foo należy niejawnie do stacktrace, i dodatkowo kiedy bar wychodzi usuwamy foo z tej listy ominiętych ramek. Bar oczywiście nie wie że może być wołane ogonowo (w szczególności jeśli jest np. funkcją z biblioteki).
Koszmar implementacyjny, duży narzut obliczeniowy, rekurencja ogonowa dalej zajmuje pamięć (tylko że na stercie a nie na stosie - próby pisana czysto funkcyjnie skończą się dramatycznym memleakiem) - w imię czego?
Dodatkowo sensowny stacktrace zawiera informacje o parametrach z jakimi funkcje były wołane. Przy normalnej rekurencji odczytywanie ich jest naturalne, przy rekurencji ogonowej te wartości znikają. Je też chcesz gdzieś zapisywać?
Oczywiście jeśli Ci bardzo na tym zależy, możesz zawsze napisać własną maszynę wirtualną która będzie wykonywała wspomniane przeze mnie operacje. Jeśli chodzi o JVM wiesz co robić pewnie, do .NET istnieje rotor, nic tylko siadać i pisać ;]