Live data from Hacker News

Tail-call optimization in C is relatively recent (2025)

lwn.net

31–40 of 129 posts

Re: Tail-call optimization in C is relatively recent (2025)

#31
post #28
post #6

>That quote is the article, and it's a little surprising that it's buried so far into the content Is it really surprising in 2026? Today's online writing style is not primarily designed to communicate. It's designed to keep the reader 'engaged' for as long as possible. The reader's time is a resource to be extracted. I'm absolutely not poking this author individually. It's the writing style of the net.

I was wondering what this was in reference to; it's not in reference to TFA here. It's a quote from https://bytecode.news/posts/2026/08/because-it-s-not-fun-eno... , so presumably you meant to post this over at https://news.ycombinator.com/item?id=49242245 .

Oh, good point, thanks.

Re: Tail-call optimization in C is relatively recent (2025)

#32

Earlier quoted context omitted.

Sounds like clang::must_tail?

I am not a Clang expert, but first, obviously that's a C++ attribute and so while Clang can decide what it means in Clang in the programming language itself it has no semantic weight because the ISO document says attributes are always ignorable. Secondly however in these languages you often won't naively get TCO because you have at least one local variable which C++ would say has a "non-trivial destructor" or Rust wo…

Reordering destructors is not safe in C++, as it's fairly common to rely on objects being destroyed in reverse order and doing stuff like

  A a;
  B b(&a);
In rust the borrow checker would guard against reordering such things, but a caveat is that there might be unsafe code relying on drop-order which the borrow checker would be oblivious to. There could also potentially be objects representing external resources like a temp file where dropping them out of order leads to issues.

Re: Tail-call optimization in C is relatively recent (2025)

#34

Earlier quoted context omitted.

This footgun is the reason I'm so enthusiastic about the Rust `become` keyword. This proposal would give Rust a specific keyword which says that you intend TCO and so two things happen: 1. The compiler goes to more length to deliver TCO even where it wouldn't "just work" and 2. If it cannot deliver TCO your code doesn't compile, because you asked for TCO.

Sounds similar to @tailrec in Scala I personally use the phrase "tail call elimination" when it's a requirement that can be relied on; and "tail call optimisation" when it might be implementation-dependent, context-dependent, limited (e.g. to immediate self-calls), etc.

I am definitely not a Scala expert.

As I wrote in a sibling comment, the key benefit here is the extra work from the compiler to deliver what you wanted, on top of the diagnostic if it can't.

I don't know if Scala has the problem that `become` addresses (C++ calls this RAII, but I have no idea what Scala would call it if they have the same idea)

However in my brief attempt to validate what Scala does do here, I found discussion of "always" optimising to a loop which is a bad sign. Tail recursion is an elegant way to write some loops but that's not the only thing it's useful for, and it seems as though Scala just doesn't care about other cases, at least for @tailrec

One thing you want TCO for in a language like Rust with lots of monomorphisation is to avoid function call overhead for the deliberately out-of-line slow path in some code. So in this case there was never an implied loop and we're not averting a stack overflow, we wanted to do a single instruction pointer change instead of an expensive function call wrapper. Seems like @tailrec isn't for that.

Re: Tail-call optimization in C is relatively recent (2025)

#35

Earlier quoted context omitted.

Sounds like clang::must_tail?

I am not a Clang expert, but first, obviously that's a C++ attribute and so while Clang can decide what it means in Clang in the programming language itself it has no semantic weight because the ISO document says attributes are always ignorable. Secondly however in these languages you often won't naively get TCO because you have at least one local variable which C++ would say has a "non-trivial destructor" or Rust wo…

Clang tail-calls aren't guaranteed to work with all C++ code. If you have a non-trivial constructor, as you mention, it will tell you this and fail instead of silently letting you believe you have tail-calls when you don't.

Re: Tail-call optimization in C is relatively recent (2025)

#36

Unless the language can guarantee TCO, I don’t feel comfortable writing tail recursive code and being at the compiler’s/interpreter’s mercy. I think the framing of TCO as an optimization has been very unfortunate.

GCC has `[[gnu::musttail]] return`.

But yes, framing TCO as an optimization is unfortunate.

Re: Tail-call optimization in C is relatively recent (2025)

#37
post #32

Earlier quoted context omitted.

I am not a Clang expert, but first, obviously that's a C++ attribute and so while Clang can decide what it means in Clang in the programming language itself it has no semantic weight because the ISO document says attributes are always ignorable. Secondly however in these languages you often won't naively get TCO because you have at least one local variable which C++ would say has a "non-trivial destructor" or Rust wo…

Reordering destructors is not safe in C++, as it's fairly common to rely on objects being destroyed in reverse order and doing stuff like A a; B b(&a); In rust the borrow checker would guard against reordering such things, but a caveat is that there might be unsafe code relying on drop-order which the borrow checker would be oblivious to. There could also potentially be objects representing external resources like a…

That Rust was in fact always unsound if it would cause problems to core::mem::drop(a); and the `become` call just drops things so it's the same.

Safe-but-undesirable outcomes are acceptable. For example maybe our tail call ends up reverting a database transaction and we wish it were otherwise. But if the code did compile but wasn't memory safe as a result of this new drop then it was always unsound and shouldn't have existed.

Just as the guts of some STL classes are very complicated in order to deliver the promised exception safety promises, the guts of unsafe Rust code are often tricky for similar reasons, you are mandated to deliver safety, it's not up to you to say "That's stupid, don't do that" either ensure it won't compile or safely cope.

Re: Tail-call optimization in C is relatively recent (2025)

#38
post #2

and TCO was added then removed from js! https://stackoverflow.com/a/54721813 This leads to fun stack-overflow bugs too in a lot of js code (one solution is to flatten: https://joshua.hu/javascript-infinite-tail-call-recursion-st... )

Js really should have it. I think the shift in style from functional and manual prototype chains to Java classes is quite disappointing.

Technically TCO is still in the spec, TC39 deadlocked over modifying it. TC39 really does not fill me with confidence in general.

Re: Tail-call optimization in C is relatively recent (2025)

#39

Unless the language can guarantee TCO, I don’t feel comfortable writing tail recursive code and being at the compiler’s/interpreter’s mercy. I think the framing of TCO as an optimization has been very unfortunate.

It's hard to argue that it isn't an optimization, because it doesn't affect the semantics of the program. However most optimizations are very hard to observe. The vast majority of optimizations only affect code size and runtime. TCO is one of the few exceptions. It affects memory usage, and more sensitive stack memory at that. This is why a missed optimization can be so much more catastrophic and it is worth considering things like `musttail` attributes so that the code fails to compile rather than misses the optimization.

I can only think of a few other optimizations that affect memory usage. Register spilling (arguably not really an optimization but a necessity), Rust's niche filling for enum discriminants and C++'s std::vec (a language-level optimization, arguably a different thing entirely).

I often think about how few memory optimizations we have. The reason is most likely that they tend to be non-local so are much harder to apply than CPU optimizations that generally have no effect outside of the function they are in.

Post reply on HN