49 comments

[ 11.0 ms ] story [ 155 ms ] thread
Does this mean that compilers have given up on processing GOTO if it is written directly by a person, but are fine in analyzing code generated in Intermediate code from GOTO-less code ?
Not at all. The compilers I'm familiar with (and my dayjob is maintaining compilers for a commercial OS) all just emit a phi node and construct basic blocks for all flow control constructs. By the time you're deep in the IR you have no idea if it was a do-loop, a for-loop, a while loop, a loop with continue and break statements, a series of deeply-nested if-statements, and aggregate initializer, or a goto.

The people who religiously avoid the use of gotos are more often than not just cargo-culters.

On the contrary. Many passes in a compiler are simpler if the control-flow graph is reducible [1], which it is guaranteed to be if the program had been written in structured programming without any gotos.

It is just that "goto" is closer to assembly code, and therefore used in intermediary code representations inside the compiler for that reason. But if the compiler had made sure beforehand that the control flow is reducible (and don't break that in an intermediary pass), then it will remain so.

I find that most uses of gotos in source code are still reducible control-flow, but which had just not been expressible using structured programming constructs in the language. For example jumping out of nested loops, "for-else" and error handling. It is very rare that you see "spaghetti code" in practice.

1: https://en.wikipedia.org/wiki/Control-flow_graph#Reducibilit...

In my youth I had a ZX spectrum which had BASIC with line numbers and no renumber command. Sometimes when adding code I'd simply run out of line numbers so had to GO TO an unused block of line numbers, put the new code there and GO TO just after the original code. I've never quite recovered from that.
My first job out of university was maintaining a FORTRAN IV program on a PDP-11. The only control structures in that language was IF..GOTO and the arithmetic GOTO. You can still write readable half-decent code with that, with discipline. A DO loop is still superior for readability.

The horrors of BASIC with its spaghetti of GO TO or its mess of PEEKs and POKEs are a justification for permabanning that style of programming -- but a decade of typing in listings from magazines inspired the generation the brought us the web and pocket phones. Maybe it wasn't such a bad thing after all.

You had a FORTRAN compiler that didn't have DO loops? I'm dubious.
FORTRAN IV (1962) predates DO, which if Wikipedia is to be trusted, was introduced in FORTRAN 66.
FORTRAN (no numbers!) had DO loops in the first release of the language. No subroutines, only statement functions, but there absolutely were DO loops! The ability of the compiler to perform what are now basic loop optimizations on them is what sold the users on automatic compilers.
Reminds me of ROM hacking. Overwriting an instruction inside a function with a branch to unused memory and jumping back later is an easy way to get extra space for the patch you want to write.
(comment deleted)
Good "Go To Statement Considered Harmful" considered harmful take. I frequently use goto in C to jump to resource cleanup code in case of an error. Always turns out more readable than the alternatives.

I think I may even have used it in Go once, but I can't remember what for. Go's defer keyword obviates the above use of goto, and labeled breaks almost any other case I can think of.

I wouldn't die on that hill but I agree and have done the same. In fact I would argue that if you find yourself in arguments about the practice it is a good sign your team is at a level where you should in fact prohibit all use of goto.

IIRC at least one of the revisions of MISRA specifically allows for using goto to break forward to a cleanup-and-return block, but others don't.

You might find splitting the functions into two works well. The outer one tries to allocate whatever resources are needed, the inner one assumes they have been allocated. Ends up looking something like (copied from one of my C codebases)

  static regex_compare_t regex_canonical_equivalent_with_structures(
      regex_cache_t *cache, stringtable_index_t x, stringtable_index_t y,
      intstack_t *stack_arg, intmap_t *map_arg) {
    // fairly complicated code using those structures
  }
  
  regex_compare_t regex_canonical_equivalent(regex_cache_t *cache,
                                             stringtable_index_t x,
                                             stringtable_index_t y) {
  
    if (x.value == y.value) {
      return regex_compare_equal;
    }
  
    intstack_t stack = intstack_create(256);
    if (!intstack_valid(stack)) {
      return regex_compare_out_of_memory;
    }
  
    intmap_t map = intmap_create(16);
    if (!intmap_valid(map)) {
      intstack_destroy(stack);
      return regex_compare_out_of_memory;
    }
  
    regex_compare_t res =
        regex_canonical_equivalent_with_structures(cache, x, y, &stack, &map);
    intstack_destroy(stack);
    intmap_destroy(map);
  
    return res;
  }
Yours seems a good practice. But apart from having concentrated it in the outer function, you have not reduced the cleanup burden. In fact, the outer function might grow out of control if the number of resources increases. In your example, should you allocate foo and bar after stack and map, you need to remember to add the respective destroy's to all failure path, and possibly in the correct reversed order (which you don't show in your code for map & stack, by the way). You'll soon find yourself with 4 identical calls to intstack_destroy() to maintain.

Instead, I'll advocate for a cleanup section at the end similar to

  cleanup:
  if (bar) bar_destroy(bar);
  if (foo) foo_destroy(foo);
  if (map) intmap_destroy(map);
  if (stack) intstack_destroy(stack);
  return res;
And I would replace all the if (!something) with, e.g.

  if (!map) {
    res = regex_out_of_memory;
    goto cleanup;
  }
In this case specifically, because the allocation failures both have the same result code, I would do the following:

  regex_compare_t regex_canonical_equivalent(regex_cache_t *cache,
                                             stringtable_index_t x,
                                             stringtable_index_t y) {
  
    if (x.value == y.value) {
      return regex_compare_equal;
    }
  
    intstack_t stack = intstack_create(256);
    if (!intstack_valid(stack)) goto out_of_memory;
  
    intmap_t map = intmap_create(16);
    if (!intmap_valid(map)) goto out_of_memory;
  
    return regex_canonical_equivalent_with_structures(cache, x, y, &stack, &map);

  out_of_memory:
    if (intstack_valid(stack)) intstack_destroy(stack);
    if (intmap_valid(map)) intmap_destroy(map);
    return regex_out_of_memory;
  }
Better yet if your data structures have "destroyvalid" methods.
Unfortunately your version doesn't free map and stack in case of success.
It's pretty. I like the tail call. Sadly jumping over the initialisation of map then calling a function on it can't work, plus the leak. Maybe introducing cannot-fail default constructors is the way to go, allowing something like:

    intstack_t stack = intstack_default();
    intmap_t map = intmap_default();
    regex_compare_t res = regex_out_of_memory;

    stack = intstack_create(256);
    if (!intstack_valid(stack)) goto out_of_memory;
  
    map = intmap_create(16);
    if (!intmap_valid(map)) goto out_of_memory;

    res = regex_canonical_equivalent_with_structures(cache, x, y, &stack, &map);

  out_of_memory:
    intstack_destroy(stack);
    intmap_destroy(map);
    return res;
It should be possible to structure things so one doesn't accumulate stack frames and also keeps the resource logic separate from the implementation but it's not immediately obvious to me what a pretty way to do that is.
In your example, there are two different places where you potentially destroy the intstack.

It's not particularly bad because there are only three possible resource allocation failures in your example, but it still illustrates a problem that will be worse when applying the approach as a general strategy. Consider a case where regex_canonical_equivalent_with_structures needed two stacks and two maps:

    intstack_t stack_a = intstack_create(256);
    if (!intstack_valid(stack_a)) {
      return regex_compare_out_of_memory;
    }

    intstack_t stack_b = intstack_create(256);
    if (!intstack_valid(stack_b)) {
      intstack_destroy(stack_a);
      return regex_compare_out_of_memory;
    }

    intmap_t map_a = intmap_create(16);
    if (!intmap_valid(map_a)) {
      intstack_destroy(stack_a);
      intstack_destroy(stack_b);
      return regex_compare_out_of_memory;
    }


    intmap_t map_b = intmap_create(16);
    if (!intmap_valid(map_b)) {
      intstack_destroy(stack_a);
      intstack_destroy(stack_b);
      intmap_destroy(map_a);
      return regex_compare_out_of_memory;
    }

    regex_compare_t res =
        regex_canonical_equivalent_with_structures(cache, x, y, &stack_a, &stack_b &map_a, &map_b);
    intstack_destroy(stack_a);
    intstack_destroy(stack_b);
    intmap_destroy(map_a);
    intmap_destroy(map_b);

    return res;
}

You now have a 5-tuple maintenance problem and the code isn't particularly readable. I call it a "wet staircase"; it's not DRY, it's easy to slip and it gets taller for each step :)

Consider the equivalent using a simple goto escape hatch:

  regex_compare_t regex_canonical_equivalent(regex_cache_t *cache,
                                             stringtable_index_t x,
                                             stringtable_index_t y) {
  
    if (x.value == y.value) {
      return regex_compare_equal;
    }
  
    intstack_t stack_a = intstack_create(256);
    if (!intstack_valid(stack_a)) goto out_of_memory;

    intstack_t stack_b = intstack_create(256);
    if (!intstack_valid(stack_b)) goto out_of_memory;
  
    intmap_t map_a = intmap_create(16);
    if (!intmap_valid(map_a)) goto out_of_memory;

    intmap_t map_b = intmap_create(16);
    if (!intmap_valid(map_b)) goto out_of_memory;
  
    return regex_canonical_equivalent_with_structures(cache, x, y, &stack_a, &stack_b &map_a, &map_b);

  out_of_memory:
    if (intstack_valid(stack_a)) intstack_destroy(stack_a);
    if (intstack_valid(stack_b)) intstack_destroy(stack_b);
    if (intmap_valid(map_a)) intmap_destroy(map_a);
    if (intmap_valid(map_b)) intmap_destroy(map_b);
    return regex_out_of_memory;
  }
Or if you add "destroyvalid" methods to your data structures,

  out_of_memory:
    intstack_destroyvalid(stack_a);
    intstack_destroyvalid(stack_b);
    intmap_destroyvalid(map_a);
    intmap_destroyvalid(map_b);
    return regex_out_of_memory;
  }
Sure, the scaling is definitely better with the goto approach. Goto will probably codegen better too (consider the destructors being inlined). Separating allocation from whatever one is doing with the resources is usually still a win though - it decouples the cases you're checking when looking through the code.
I disagree about the examples of when a go-to is useful:

> - Using a single goto and single label to exit several levels of scope.

Better handled by putting the nested scope in a function and using `return`.

> - Using a goto in the middle of complex construction to short cut to the top of a loop.

Use `continue`.

> - Starting a highly optimized do { ... } while loop that is best initiated by jumping into the center first. (Because your compiler is not cooperating.)

This one's tricky, first be sure that the optimization is actually needed. Then, with a quick glance at DRY for apology put a copy of the second half of the loop before the `do`.

> - Extensions of duff's device.

Oh come on. The original Duff's device is not intuitive when first approaching it, and there is pretty much one use for it. I wouldn't even suggest to juniors that they be aware of this trick at all, tbh. It's a curiosity but I balk at it being a justification for adding gotos to make your own custom extension of it. See above, check that you really need such a severe optimization. You likely don't.

>> - Using a single goto and single label to exit several levels of scope.

> Better handled by putting the nested scope in a function and using `return`.

I dunno, that seems pretty silly to me. It's a perfectly valid use case of goto to jump out of deeply nested loops, and refactoring it to break out the inner loop in a function seems like pointless busy-work that ultimately makes the code harder to read, just so you can avoid a goto.

@OskarS I agree with you... In fact, I have been avoiding gotos as a matter of principle for years and now reading the different discussions, I think I was misled. This case is very, very common. You have 3 intertwined loops and you want to get out. The traditional solution is to add some intermediate variables to propagate the end of the loops, with a cascade "if (toto) break;" which are far from being exquisitely elegant... Using a function for that is really adding some more complexity and some more stuff on the stack for nothing.
Also as a matter of technology, using "if" to get out of loops is bad with modern processors and their internal predictive algos.

My solution for a long time was to add the test within the "for" itself, but it is often confusing for users.

Exactly. The only reason not to use goto here is because of a dogmatic opposition to ever using goto. In fact, this is a rare case where goto makes the flow of the program more clear rather than less clear, and alternate solutions are just worse.
> put a copy of the second half of the loop before the `do`

But why is that better than just using a goto? Goto’s are usually discouraged because they make code hard to follow, but if avoiding the goto makes the code even more convoluted, whats the point?

Given that exposure to `goto` is going to be minimal born most developers born after 1980's, the use of `goto` is likely to appear more convoluted.

At any rate, the greatest danger in this case lies in the code duplication. If performance is not an issue, stick it in a function. If performance may be an issue, start off by sticking it in a function and see if compiler optimizations take care of the issue. If performance is an issue, by all means force it to do what you want by using `goto`.

While there is nothing intrinsically evil about `goto`, most of the developments in programming languages were meant to overcome challenges of the past. We shouldn't dump those developments based upon exceptions to the rule. Rather, we should recognize exceptions to to rule as being extraordinary circumstances in which those developments were not useful.

> start off by sticking it in a function and see if compiler optimizations take care of the issue.

You could also just use a goto and avoid the accidental complexity.

I still dont understand why a complicated workaround is better than a simple goto.

I've been working my way through the latest volume of The Art of Computer Programming (Volume 4B, released last year) which is about combinatorial algorithms. One of the very first algorithms he presents Algorithm B, "Basic backtrack". It's like a generic backtracking algorithm that you could use to solve combinatorial problems (e.g. n-queens, things like that).

Every time I've seen this algorithm presented, it's used recursion, because that's a very natural way to express backtracking. But Knuth doesn't, he basically never uses recursion in his TAoCP algorithms, he expresses his algorithms in a "flow chart" style, not assuming things like stacks or subroutines (in fact, one of the exercises in the chapter is to convert Algorithm B to a recursive version, which he then quickly dismisses in the answer to the exercise as "elegant, and works find for small problems" but fundamentally not very high performance and not really clearer than the non-recursive version).

So, I set down to implement Algorithm B in a generic way and used it to solve n-queens. The way Knuth has written it, it can be straightforwardly translated using `goto`s (it has lines like "if so-and-so, go to step B4, else B3"), but being the modern, structured programmer that I am, i tried to convert it to structured programming using loops and if statements.

It was doable, but when benchmarking, my versions were always slightly slower than Knuth's original "goto" formulation, and honestly I don't think they were more readable either. You had to add a bunch of variables to keep some state around that was implicit in Knuth's version. The recursive version was definitely more readable, but Knuth was of course correct, you pay a performance penalty for that readability.

It was a real eye-opener. In the hands of a master, "goto"s really do have a place, and when well-deployed, they can sometimes simply be superior to "structured" code using loops, ifs and recursion.

One of the arguments un favour if Tail Calls Elimination it that it allows to implement a goto-state-machine with mutally recursive functions.
Yeah, this is a really interesting point, I should try this again with [[musttail]] on clang.
My understanding is that while it is possible to compile Tail Calls to be as efficient as an inline function it is (often) not possible in C.
But the goto state machine is no easier to understand than the one with gotos.

There was an argument like this on the comp.lang.c newsgroup a few years ago. Someone proposed a coding problem: I think it was writing a program that removes comments from C source, but preserves preprocessing directives and all else.

So various solutions were posted.

I wrote one that used a state machine based on goto; no control structures at all beyond assigning to variables and if/goto.

That original poster then revealed his solution. It consisted of mutually tail calling functions.

He specifically criticized the goto graph solution.

I claimed they are basically the same, and to bolster my argument, I wrote a trivial simple text filter which converted that tail calling solution to a goto graph.

Tail calling is not a higher level feature compared to goto. It is only syntactic sugar which leaves the program with the same organization.

In some ways, the goto program is easier to understand. Why? Because:

1. The variables of the graph are all declared together in one place. Say you have a local variable x. All goto graph blocks that refer to x are referring to the same thing. In the tail program, every function that does something with x has to declare it for itself as its own parameter. It's not obvious that those x's are supposed to be the same entity.

2. Typically, functional languages, like C, do not have named parameters. The state is passed among the tail-calling function via strictly positional arguments. Not all functions have all the state variables, so a given state variable could be argument 3 in one function or argument 1 in another function.

In the goto program it's very clear. If you have

   bar:
     x = s + 1;
     goto foo;
we know: oh okay, this block updates the x state from s, and goes to foo. In the functional version:

  bar(s) -> foo(s + 1)
we don't know what s + 1 is doing. We have to read foo:

  foo(x) -> ...
ok, we know that s + 1 will be called x in foo. But is that consistent? Is it the same as every other x we see everywhere in the tail call graph?

We easily see that consistency in the goto graph: there is a single x variable in the scope of the entire graph; anything that assigns to x is preparing a new value for that x.

> In some ways, the goto program is easier to understand. Why? Because:

> 1. The variables of the graph are all declared together in one place. [...]

> 2. Typically, functional languages, like C, do not have named parameters. [...]

I have little experience in C, but probably I would try to use a struct for the machine state.

Even with no tail calls I think I would try to write the machine as mostly block of

    bar:
      step_bar(&state);
      goto foo;
From my optic the benefit of tail calls is the ability to mix flow control and machine steps with function abstractions.
Yes; or well:

  void step_bar(state *state)
  {
    state.x = state.s + 1
    return step_foo(state)
  }
This is where the single dispatch OOP with implicit class-scoped "self" reduces noise:

  void state::step_bar()
  {
     x = s + 1;
     return step_foo();
  }
You have the tail calls, all variables in one place: x is always the same x. You know which variables are being set in each state transition; no need to look through parameter positions in function definitions.
I think the 'debate' only makes sense in a sufficiently low-level language, at which point you're already taking safety into your own hands.

In a higher-level language, it's as silly as asking if you should avoid explicit malloc/free when writing SQL.

How would the Rust compiler deal with goto? You could dodge ownership and start operating on data before/after it's created/deleted.

A try-with-resources/using in Java/C# would no longer offer its guarantees.

How about async blocks in general? Do you just wander into another thread's code (but keep your own stack frame?)

Lambdas? Closures?

People probably

> regurgitate the age old argument against the use of goto

because there's no new arguments for it.

C# goto does not let you break `using var ...` guarantees or violate scopes of other control flow constructs.
It is really a post about software dogma.

GOTOs are just one of the oldest ones, but these days, for example, we have polymorphism, and OO, in general, and, for some folks, anything that is \(!$HOLY_MANNA) is bad.

In my work, I tend to mix brand-new techniques with very old ones (probably older than some of the folks reading these words of prose).

Writing Solid Code was written in 1992. Many of its techniques have been integrated into toolchains, some are outdated, and some are still every bit as valid, today, as they were, then.

I will say that reading it was a watershed, for me.

The goto of goto considered harmful is not the goto of C. Somehow this subtlety seems to be broadly missed.

The goto of Dijkstra jumps to somewhere else in the program. You're somewhere in the call stack and decide to scribble on some registers and jump to some arbitrary location. Like longjmp with the restrictions taken off. As in across function boundaries. When you're writing assembly that works exactly as it it sounds like it would.

The goto of C jumps to somewhere else in the same function with a bunch of compiler warnings if you manage to bypass an initialisation. You can make control flow more complicated within a single function, sure.

Goto in C is not a significant problem. Jumping between different functions in assembly is however legitimately confusing.

This is something of a pet annoyance because various languages or coding standards decide to outlaw the friendly easy goto as it has such a scary reputation. This makes some algorithms much more complicated to express and others inherently slower.

I don't believe I came up with this one, but I haven't been able to source it: for a modern audience, "Go To Statements Considered Harmful" should probably be titled "Structured Programming Considered Beneficial". Structured programming was (comparatively) new, and advocating for it made sense, given that FORTRAN and COBOL were the major languages in use, and (at the time) didn't exactly encourage the style.
Someone made a similar claim in HN recently. I reviewed Dijkstra's paper and it doesn't bear out the claim. The C goto falls squarely under the scope of the paper.

Dijkstra's paper is not strictly focused on a global, machine-language goto that can branch anywhere in the address space.

In fact, Dikjstra mention that he would also like to banish statement like while B repeat A and repeat B until A, because we have recursion, which makes them unnecessary. He lets them slide only for reasons of "realism" and because such loops are analyzable with inductive techniques.

There is imprecision in my wording. The paper was about removing goto from high level programming languages. The context in which people were starting to view goto with suspicion was that of ~1968, at which point I believe the baseline assumption was that goto was a branch to anywhere as that's what assembly gives you. However it was before my time and I'm inferring from partial information.

I think the distinction between goto as rendered in C and goto as rendered in machine code is significant. It's the same argument in favour of structured programming in both cases. The gap between structured programming and local goto within a function is smaller than in the assembly case.

Algol 60 already had a go to statement, restricted to jumping to local labels, similar to the one in C.

It is only my speculation, but it seems very likely that Dijkstra had the Algol 60 and Algol 68 go to statements in mind. As in, an example high level language from which to banish the go to statement would have been whatever successor is planned for Algol 68.

Note, the common reason people fail to comprehend what Dykstra was explaining, is because people don't realize they are almost universally adopting one of the three major programming paradigms.

While people are familiar with Functional and OOP, most forgot or don't know about structured programming. Because it has proven to be good by default almost universally.

Look at the COBOL ALTER statement or GCC computed GOTO to see what the issues were.

While there are Böhm–Jacopini theorem purists that argue break and return, it is the law of diminishing returns.

Break and return may be syntactic sugar for GOTO, but they don't have the same problem of the unrestricted transfer of control that was and is considered harmful.

GOTO is useful when used in the right context. Heavy use of exceptions are more of a problem today.
> Update: I recently looked at the book "Compilers Principles, Techniques, and Tools". In it, it describes the intermediate languages that programs are compiled to in which typically all branches are transformed to goto and if ... goto constructions. The idea being that optimizations are done after the program has been transformed to a loopless goto riddled intermediate program. That seals it for me -- I say use goto whenever it makes sense.

That same book also teaches something important: programs based only on control structures like if and while generate goto graphs that meet a certain important property. All non-forward gotos in the graph are backwards gotos, dominated by the statement to which they branch.

A situation like this isn't generated by control structures; it requires goto:

  if (condition0) {
     statement1;
  label0:
     statement2;
  }

  if (condition1) {
    goto label0;
  }
What happens now that statement2 is reachable in a way that doesn't require/entail the execution of statement1.

The "goto label0" is not a true backwards goto.

Backwards gotos return to nodes which dominate the goto statement; i.e. go back to a point in the program that had to be visited in order to reach the goto. Here, label0 doesn't dominate the goto at all. The goto can be reached without reaching that label.

I haven’t written a GOTO since 1992, when I needed a common exit to free memory that couldn’t be accommodated on the tiny 4K stack of Turbo C / 16-bit.

I have never experienced the need for a ‘goto’ otherwise.

Now, 1970’s COBOL, that was another story.