Достаточно легко попробовать и посмотреть, что получится:
-module(loop).
-compile(export_all).
loop() ->
receive
{drop, X} -> drop(X);
after 1000 ->
erlang:display(catch erlang:error(noes)),
drop(ok)
end.
drop(X) ->
case X of
ok -> loop()
end.
Если вы запустите loop: loop (), вы увидите, что он действительно не увеличивает стек. Если вы добавите a, 1 после вызова drop (ok) или loop (), вы увидите, что стек растет.
Итак, компилятор выясняет, что это хвостовой вызов, и оптимизирует его, даже если это не рекурсивный хвостовой вызов.