Skip to content
Permalink
Branch: goto
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
113 lines (80 sloc) 3.73 KB
  • Feature Name: goto
  • Start Date: 2016-12-01
  • RFC PR: (leave this empty)
  • Rust Issue: (leave this empty)

Summary

Add a limited, principled, form of the goto statement and labels. Certain control flows, while expressible are exponentially more verbose without it. Adding goto should also satisfy most use-cases for guaranteed constant-space tail calls.

Motivation

Why are we doing this? What use cases does it support? What is the expected outcome?

Detailed design

Syntax

First an example, showing the mix of functional and imperative styles this design enables:

fn foo(a: u32) {
    assert_eq!(loop {
        goto 'foo();

        label 'foo() {
            match a {
                0 => goto 'bar(a),
                _ => { a -= 1; continue },
            }
        }

        label 'bar(b: u32) {
            break b + 1
        }
    }, 1);
}

I've prototyped the precise grammar changes at https://github.com/Ericson2314/rustypop/tree/goto. The most important idea is that any block can now contain statements (at least one) followed be these "label blocks" (also at least one). Note there can be no final expression in this case. Label blocks themselves generally mirror functions. They differ in that

  • they start with label instead of fn
  • their names are prefixed "'".
  • cannot have type parameters
  • cannot have a explicit return type, because they represent continuations

goto '<label>(<arguments>) is a new expression form. Just like with function and function calls, the parenthesis are mandatory: label bar() { .. } is a label block with zero parameters, and goto bar() is the goto expression with zero arguments.

Every label name is in scope everywhere in the block contain the binding label block. For forwards-compatability with later extensions however, no two labels in a function can share the same even if they are not in the same scope.

Type rules

  • goto expressions, like their break and return cousins, always has type !.

  • label blocks' bodies also always have type !.

  • return expressions in label blocks break out of the enclosing function, and thus do not interact with the type of the label block.

The rest of the Semantics

One can only goto a label block in scope (defined in an enclosing block). Actually, as the MIR only has function-local variables, this rule isn't really necessary. But relaxing this restriction probably just puts needless cognitive burden on users. We can always relax it in the future.

there shouldn't be too many issues desugaring this. However it's harder to reason about, and isn't strictly necessary to achieve the goal of less function blowup

Drawbacks

  • As goto subsumes all other control foo constructs, it could cause confusion on what's the best approach.

  • It's useless to put labal blocks in non-body blocks...unless we can break out of arbitrary blocks.

Alternatives

  • Lighter syntax, e.g. no curly braces or label.

  • Allow goto to label block not defined in enclosing block. As the MIR only has function-local variables, there shouldn't be too many issues desugaring this. However it's harder to reason about, and isn't strictly necessary to achieve the goal of less function blowup

  • "Expression destinations". a label is in fact an expression, so one can write code like

    goto foo(1); bar(labal foo)`
    

    which would be equivalent to

    bar(1)`
    

    Neat, but confusing and probably not very useful. I think this is the wrong way to "functionalize" goto.

Unresolved questions

  • How do goto label names overlap with loop label names?
You can’t perform that action at this time.