Functions cannot "fall off the end" and start executing code outside of their scope, regardless of whether they return a value or not. It's common to allow the final return statement to be omitted for functions which do not return a result (or in some undisciplined languages even for functions which do return a result), but the function needs to return in all cases.
Given that, the simplest way to produce error messages for non-void functions which fall of the end is:
- The parser inserts a - returnstatement at the end of the function body.
 
- Dead code elimination is used to remove the inserted - returnstatement in case it is unnecessary.
 
- Typechecking is used to verify that all - returnstatements, included the inserted one if it hasn't been deleted, are consistent with the return type of the function. (That is, since the generated- returnstatement has no value, it can only be consistent with a- voidfunction.)
 
In this scenario, you need to delete unnecessary return statements before typechecking, or you'll end up with a lot of incorrect error messages. That requires some control flow analysis.
If that's too much work, you can issue the error message at run-time by compiling the no-value return statement as an error operation in the case that the function returns a value.