16 comments

  • swiftcoder 3 minutes ago

    > In 2001 Mark Probst implemented tail-call optimization in GCC

    MSVC didn't add tail-call optimisation until sometime in the 2010s, IIRC.

    I distinctly remember sending a tail-recursive C++ program to someone who developed on Windows, and it crashing, in the late mid-to-late 2000s.

  • kenjin4096 an hour ago

    I think Anton is replying to me in that LWN article IIRC. I personally didn't know C only had tail calls that late and learnt something new there!

    On the other hand, I am pretty new to the compiler space myself, and I count early 2000s as a pretty long time ago, though again it is not that far back considering how long other language implementations had tail calls like in ML or variants since 1980-90s.

  • nyeah an hour ago

    >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 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.

  • mmsc an hour ago

    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...)

    • groundzeros2015 40 minutes ago

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

    • pfdietz an hour ago

      Lack of TCO is also a common footgun for Scheme programmers using Common Lisp.

      • pjmlp 12 minutes ago

        Mostly because they forget Scheme is one of the few languages where TCO is part of the language standard, making it a required feature for any compliant implementation.

        This has always been an issue regarding TCO support across programming languages.

      • tialaramex an hour ago

        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.

        • chriswarbo 43 minutes ago

          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.

        • StilesCrisis an hour ago

          Sounds like clang::must_tail?

          • tialaramex a minute ago

            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 would say "implements Drop". These both mean that naively the "tail call" wasn't actually the last thing to happen, the destructor / Drop::drop happen at the end of the function, after the tail call.

            The proposed become keyword tries to core::mem::drop any such variables, if it succeeds now that tail call is last and we can do TCO, if it fails we can diagnose the problem. I believe the Clang attribute doesn't have this behaviour.

      • guenthert an hour ago

        Only if they are using an insufficiently smart compiler. SBCL handles TCO just fine, as do a number of other implementations, see : https://0branch.com/notes/tco-cl.html

        • pfdietz 42 minutes ago

          Even SBCL doesn't do TCO at all times. Compiling at (debug 3) means no TCO.

          Another related footgun is deep recursion of other kinds, for example when recursively traversing down lists. For long lists it's easy to exceed the stack size limit. The common idiom is to recur on list elements, but iterate or map to go along a list.

          • guenthert 32 minutes ago

            > Even SBCL doesn't do TCO at all times. Compiling at (debug 3) means no TCO.

            Presumably one intends to debug the code, when setting (debug 3). Then it'll be helpful to see the stack, no?

            > Another related footgun is deep recursion of other kinds, for example when recursively traversing down lists. For long lists it's easy to exceed the stack size limit. The common idiom is to recur on list elements, but iterate or map to go along a list.

            Not going to argue with seasoned lispers here, but IMHO recursive code makes most sense when accessing recursive data structures.

  • messe an hour ago

    > In 2001 Mark Probst implemented tail-call optimization in GCC with a separate calling convention; he lists the limitations of the then-existing tail-call optimization in GCC in section 6.4, among them: "It cannot handle indirect calls" (which would have been used in tail calls for interpreter dispatch).

    Relatively recent being a quarter of century? Or at least a fifth of a century for indirect calls[1] (GCC 3.4.6 is the earliest I see on Compiler Explorer, released March 2006).

    [1]: https://godbolt.org/z/vvcnn54oM