Cursed fallthrough pattern

I recently encountered this pattern, which we've been calling a "cursed fallthrough"

func f(f: Bool, g: Bool) {
    switch f {
    case true:
        print("f.true <begin>")
        if g {
            print("g.true")
        }
        else {
            print("g.false")
            fallthrough
        }
        print("f.true <end>")
    case false:
        print("f.false")

    }
}

f(f: true, g: false)

It surprised me that fallthrough is even permitted in this position, and was not obvious to me where control flow would even "fallthrough to".

If you squint, you can find a description of this behavior in the documentation:

The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block,

which, read in isolation, sounds like what happens. However the sentence continues on to provide a context,

as in C’s standard switch statement behavior.

...which is not any kind of mental model for this function.

I imagine the language semantics are somewhere between "intentional" and "too late to change it now" (I got a PR with the pattern this morning), but I wonder if the documentation ought to be changed to introduce fallthrough as a control flow statement that jumps, rather than a statement you put at the end of your case to get C semantics.

1 Like

C++17 has the [[fallthrough]] null line construct that is the same behavior as the Swift fallthrough statement when executed from inside the code of the case section, for example, within an if-then block as shown in the Swift example above. C11 is considering adding the construct, or may have already added the construct.

I'm a fan of how fallthrough is currently presented in the language guide because it contains the right amount of information about how Swift switch statements work and they're different than in some other languages you might know. The more technical description in the reference matches your request for a place to go to read that the pattern you don't like is allowed.

1 Like

I think @Drew_Crawford1 has a point. The sentence referred to:

The fallthrough keyword simply causes code execution to move directly to the statements inside the next case (or default case) block, as in C’s standard switch statement behavior.

is nonsensical, because there is no C statement to be "as in" Swift. There is only as-ness when the fallthrough happens to be at the end of its case, which is a special … um … case. In addition, C's standard behavior isn't well described as "move to". It just falls through**. (Similarly, we'd never say that an assignment "moves directly to" the next sequential statement when it's done. It's not false to say so, just more confusing to say than not.)

The documentation note would be better without that last clause referring to C. The actual behavior is much better described in the main body of text in the preceding paragraphs.

** So, paradoxically, Swift's fallthrough is named after C's behavior, but doesn't actually have C's behavior.

1 Like