1

I have noticed that when I write the following code, the compiler yields a missing return statement error:

// Similar loops make sense in retry patterns
// but this is just a simple example
func TestMethod() int {
    for i := 0; i < 10; i++ {
        return 0
    }
}

this is while the following compiles without any error:

func TestMethod() int {
    for {
        return 0
    }
}

The fist code is both logically and technically fine as there is no possible way the method cannot return. Is there any reason why the compiler is showing that error? or it is some sort of a missing logic or bug?

6
  • 5
    This error is perfectly logical. Whenever you are using loop's there is no guarantee that the code within the loop would even execute. Sure in your case it would but compiler can't know that for sure therefore the mentioned error is raised. I'm not sure about GO but in Delphi you would assign some default value to function result first and then change that result value within the loop if certain conditions are met. Commented Jan 8, 2024 at 4:46
  • @SilverWarior the Delphi comparison isn't quite equivalent. Assigning a function result and returning from the function with a result are very different things (the latter exits, effectively terminating, the loop where the former does not). The Delphi equivalent would be to assign a result value (regardless of whether a default result has previously been assigned) and then call exit. In more recent Delphi versions you can combine the two steps by calling exit with a parameter that provides the desired result value: exit(x), which is exactly equivalent to return x. Commented Jan 8, 2024 at 6:12
  • @SilverWarior: Sure in your case it would but compiler can't know that for sure, I know that may sound like perfectionism but I guess the compiler must be able to detect that case. I guess C# compiler well detects that. Again, this is not something important but it was a cool thing to know about Go. This makes a good exam question as well. Commented Jan 8, 2024 at 6:17
  • @CeriseLimón: That's right but as I said the presence of that feature to detect the return is something nice but Go remains an amazing language. You know when making expression trees, similar to AST, it is not that hard to tell if something is a variable or constant. In this case the condition is based on a constant that can be easily detected by the compiler. (reference: I have been writing compilers and have written a few DSLs). However, I agree with you that sometimes it may get complicated and it is safer to not implement that functionality at all. Commented Jan 8, 2024 at 6:56
  • @CeriseLimón: In other words, writing a thousand line of code just to be able to detect where the function returns in a loop can make the compiler code suffer. So I agree with you. Commented Jan 8, 2024 at 6:58

1 Answer 1

6

Expanding @Cerise Limón's comment into an answer, the assertion that "the first code is .. technically fine" is wrong.

The Go language specification says this:

If the function's signature declares result parameters, the function body's statement list must end in a terminating statement.

And also this:

[terminating statements include] a "for" statement in which:
- there are no "break" statements referring to the "for" statement, and
- the loop condition is absent, and
- the "for" statement does not use a range clause.

(emphasis added by me)

Examining the code in the first function we can see that these conditions of the specification are not met:

func TestMethod() int {
    for i:= 0; i < 10; i++ {
        return 0
    }
}

The function has a result parameter (the int return value) so must end in a terminating statement, but the final statement of this function is a for statement with a condition, which is not a "terminating statement", as defined by the specification.

It may seem odd but is, in fact, technically correct.

Bonus Material

So why is the second function fine?

func TestMethod() int {
    for {
        return 0
    }
}

In this case, the final statement in this function is a for with no condition and with no break statement referring to the for loop, satisfying the language specification definition of a terminating statement.

There is logic at work.

If a for statement with no condition contains a break, then the loop may terminate so the function needs a return statement.

If a for statement with no condition does not contain a break (and has no return statements), then the loop will not terminate (at least, not as the result of a normal execution path that requires a function return value).

Also worth noting is that there is no control flow analysis to determine whether any break statements are reachable; they only need to exist. e.g. the following will also trigger a "missing return" compilation error, even though the break is demonstrably unreachable:

func foo() int {
    for {
        if false {
            break
        }
        return 0
    }
    // <-- error: missing return
}
Sign up to request clarification or add additional context in comments.

1 Comment

+1. It may be worth noting that it's literally impossible for a compiler for a Turing-complete language to determine, in all cases, whether a given line of code is potentially reachable. So the spec can't demand perfection. And since the intention is to have consistent behavior across the tool chain, the spec needs to specify exactly what cases count in each direction, and is motivated to specify something reasonably simple that's "good enough" instead of something complicated that still wouldn't be perfect.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.