godotengine / godotengine/godot
GDScript must raise error when an empty `return` is used on any non-`void` function (and on `return value` on `void` functions)
- Dominant language
- C++
- Stars
- 117k
- Forks
- 26.8k
- PR merge metrics
- PR metrics pending
Description
### Tested versions
- Reproducible in: v4.4.1.stable.flathub [49a5bc7b6]
- Not Reproducible in: v3.6.stable.official [de2f0f147]
### System information
Godot v4.4.1.stable (49a5bc7b6) - Freedesktop SDK 24.08 (Flatpak runtime) on X11 - X11 display driver, Multi-window, 2 monitors - OpenGL 3 (Compatibility) - Mesa Intel(R) HD Graphics 5500 (BDW GT2) - Intel(R) Core(TM) i5-5300U CPU @ 2.30GHz (4 threads)
### Issue description
I'm in disbelief that I just found out this ... an error check everyone would expect (found in every other programming language AFAIK), but is not present in GDScript. I was (very reasonably) assuming it existed, but now I feel like I have to second-guess all of my code - who knows if it doesn't have a hidden bug similar to what I encountered?
#### Functions with an `Object`-derived return type are accepting an empty `return` statement
The empty `return` is being treated no different then a `return null`. This is absolutely a bug (specially since it seems to not happen in 3.6). If this was by design, then it's wrong too. If you forget to add the return value, you get no compile-time error, not even a warning.
Yes, the [docs](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#functions) do say:
> A function can `return` at any point. The default return value is `null`.
While this is (sort of) understandable for dynamic typing (since "void" is treated as a null-only type in GDScript), at least when the return type is explicitly added, 10 out of 10 coders will expect this check, and assume the code is correct, even if they know void is treated as null, because it's a syntactic check. This is likely already leading to hard-to-find bugs. It's specially easy to assume it exists since non-object return types already have this check (as they don't accept `null`). Even if void functions just return null under the hood, they are conceptually different, and this distinction needs to be a compiler check to help coder make less mistakes.
The docs also say:
> Functions that have a return type **must** return a proper value. [...]
> Non-void functions must **always** return a value, so if your code has branching statements (such as an if/else construct), all the possible paths must have a return. E.g., if you have a return inside an if block but not after it, the editor will raise an error because if the block is not executed, the function won't have a valid value to return.
So there is an intention to prevent this mistake, but it seems the compiler only checks for the presence of a return statement, but it's not enforcing that it must have an explicit value for non-`void` functions. This check needs to be added, specially since ~~the opposite check exists~~ (if you add a value to a void function return; EDIT: it seems to be only for non-object values, the opposite is an issue too).
At the very least, the check needs to be added where it's doable as a simpler syntactic check:
- check on functions with a typed return value;
- also forbid mixing `return` and `return value` in the same function, even if it has no explicit return type; if someone does this, it's almost certainly a mistake, and it should not be allowed; if you intended to return null on a function that could also return a non-null value, then you should have to write `return null` explicitly;
But I would recommend further analysis to prevent mistakes if possible:
- e.g.: also check on overriden functions without the explicit type, when the parent is statically typed;
To summarize, the compiler should classify every function as either void or non-void, and enforce the appropriate return syntax. Ideally, even if return type isn't provided explicitly, the intent should be inferred from the parent or existing syntax; you should prevent mixed return syntax too.
By the way, it seems the opposite issue exists too: **void functions are not always enforcing empty return statements**.
### Steps to reproduce
This seems to raise errors correctly on 3.6, but not in 4.4.1:
```gdscript
func new_obj() -> Object:
return # not giving even a warning! this must raise an error here when the explicit null is absent
func create_obj() -> void:
return Resource.new() # this should also raise error instead of a simple "value discarded" warning
# example of buggy code because of a simple oversight:
var _cached_resource: Resource
func get_res() -> Resource:
if _cached_resource != null: return # forgot to add _cached_resource after return
var r: Resource = Resource.new()
# load_res(r)
_cached_resource = r
return r
```
### Minimal reproduction project (MRP)
N/A
Contributor guide
Research direction
Start by reproducing the examples in the issue on Godot 4.4.1, covering empty returns in Object-typed functions and value returns in void functions. Trace how the GDScript compiler validates return statements and compare the documented rules with the observed behavior. Done means the invalid forms raise compiler errors, while valid explicit returns continue to work, with regression coverage for the reported cases.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100