nullable static types documentation requested. - #175
Conversation
📝 WalkthroughWalkthroughThe PR adds a GDScript nullable-types tutorial. It covers syntax, supported types, null narrowing, warnings, runtime enforcement, typed containers, objects, and enums. The GDScript tutorial index links to the new page. ChangesNullable types documentation
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tutorials/scripting/gdscript/gdscript_nullable_types.rst`:
- Around line 207-210: Update the freed-object explanation near the nullable
GDScript example to state that node == null is not a general freed-object check
because a freed reference may remain non-null. Identify is_instance_valid(node)
as the required check for determining whether the object still exists, and
remove the contradictory claim that node == null reliably detects freed objects.
- Around line 20-24: Update the nullable-types documentation wording in both
referenced sections, including the UNSAFE_NULLABLE_ACCESS description, replacing
“built-in value types” with “non-object built-in types.” Ensure the examples
distinguish reference-backed Array and Dictionary from value types such as int
and String.
- Around line 6-16: Update the nullability explanation in gdscript_basics.rst to
state that Variant already permits null, while statically typed built-in types
are non-nullable by default and require the ? suffix to accept null. Align the
wording with the behavior documented in gdscript_nullable_types.rst.
- Around line 188-200: Update the “Notes on objects and enums” section to
explicitly state that direct member access or method calls on a null
nullable-object reference can still fail at runtime, despite no warning being
emitted. Add a narrowed GDScript example using `if node != null:` before calling
`node.some_method()`.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 88590fdd-d3cc-49bc-8879-54c2ffddf9ad
📒 Files selected for processing (2)
tutorials/scripting/gdscript/gdscript_nullable_types.rsttutorials/scripting/gdscript/index.rst
| This guide builds on :ref:`doc_gdscript_static_typing`. In statically-typed | ||
| GDScript, a value is **non-nullable** by default: the type system flags ``null`` | ||
| as an illegal value for it. Writing a ``?`` after a type hint makes the value | ||
| **nullable**, opting it back into holding ``null``:: | ||
|
|
||
| var health: int? # May be an int or null; starts as null. | ||
| var player_name: String? = null | ||
|
|
||
| This is purely a static typing feature. It does not change how values are stored | ||
| at runtime — ``null`` is still ``Nil``. It changes what the analyzer allows and | ||
| what the virtual machine enforces. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 'Only types that inherit from Object|Variant types.*null' tutorials/scripting/gdscript/gdscript_basics.rst
rg -n -C 4 'int\?|String\?|Array\?' tutorials/scripting/gdscript/gdscript_nullable_types.rstRepository: Redot-Engine/redot-docs
Length of output: 5549
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- gdscript_basics.rst ---'
sed -n '680,705p' tutorials/scripting/gdscript/gdscript_basics.rst
printf '%s\n' '--- gdscript_nullable_types.rst ---'
sed -n '1,75p' tutorials/scripting/gdscript/gdscript_nullable_types.rst
sed -n '130,180p' tutorials/scripting/gdscript/gdscript_nullable_types.rst
printf '%s\n' '--- related nullability wording ---'
rg -n -C 3 'Only types that inherit from Object|Variant types|nullable|non-nullable|Array\?|Dictionary\?' tutorials --glob '*.rst'Repository: Redot-Engine/redot-docs
Length of output: 23497
Update the nullability description in gdscript_basics.rst.
The statements at lines 695–698 conflict with the nullable static types documented in gdscript_nullable_types.rst. Explain that Variant already includes null, while statically typed built-in types require the ? suffix to accept it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tutorials/scripting/gdscript/gdscript_nullable_types.rst` around lines 6 -
16, Update the nullability explanation in gdscript_basics.rst to state that
Variant already permits null, while statically typed built-in types are
non-nullable by default and require the ? suffix to accept null. Align the
wording with the behavior documented in gdscript_nullable_types.rst.
| How strictly "non-nullable" is enforced depends on the type. Built-in value | ||
| types (``int``, ``String``, ``Array``, and so on) are checked both when used | ||
| directly and at runtime. Enums are checked on transfer and at runtime. Object | ||
| references (``Node``, ``RefCounted``, script classes) keep GDScript's existing | ||
| behavior and may hold ``null`` regardless of the ``?``. See |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 'Built-in value types|Array\?|Dictionary\?' tutorials/scripting/gdscript/gdscript_nullable_types.rst
rg -n -C 3 'reference type|value type|Array|Dictionary' --glob '*.rst' --glob '*.cpp' --glob '*.h' .Repository: Redot-Engine/redot-docs
Length of output: 50380
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- nullable-types excerpt ---'
sed -n '1,175p' tutorials/scripting/gdscript/gdscript_nullable_types.rst
printf '%s\n' '--- basics terminology ---'
rg -n -C 4 'passed by reference|passed by value|value type|reference type|Array and Dictionary|Array.*Dictionary' tutorials/scripting/gdscript/gdscript_basics.rst
printf '%s\n' '--- focused nullable references ---'
rg -n -C 5 'Built-in value types|non-object|Array|Dictionary' tutorials/scripting/gdscript/gdscript_nullable_types.rstRepository: Redot-Engine/redot-docs
Length of output: 12956
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- remaining nullable-types excerpt ---'
sed -n '175,225p' tutorials/scripting/gdscript/gdscript_nullable_types.rst
printf '%s\n' '--- deterministic terminology check ---'
python3 - <<'PY'
from pathlib import Path
basics = Path("tutorials/scripting/gdscript/gdscript_basics.rst").read_text()
nullable = Path("tutorials/scripting/gdscript/gdscript_nullable_types.rst").read_text()
needle = "Built-in types are stack-allocated. They are passed as values."
start = basics.index(needle)
end = basics.index("Basic built-in types", start)
print(basics[start:end].strip())
for term in ("Array", "Dictionary"):
print(f"{term}: nullable note={term in nullable[nullable.index('How strictly'):nullable.index('Syntax')]}; "
f"direct-use section={term + '?' in nullable[nullable.index('The UNSAFE_NULLABLE_ACCESS'):nullable.index('Runtime enforcement')]}")
PYRepository: Redot-Engine/redot-docs
Length of output: 2591
Replace “built-in value types” with “non-object built-in types” in both sections. Array and Dictionary are passed by reference, unlike int and String. The current wording incorrectly classifies Array as a value type, including in the UNSAFE_NULLABLE_ACCESS description.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tutorials/scripting/gdscript/gdscript_nullable_types.rst` around lines 20 -
24, Update the nullable-types documentation wording in both referenced sections,
including the UNSAFE_NULLABLE_ACCESS description, replacing “built-in value
types” with “non-object built-in types.” Ensure the examples distinguish
reference-backed Array and Dictionary from value types such as int and String.
| Notes on objects and enums | ||
| -------------------------- | ||
|
|
||
| Object references (``Node``, ``RefCounted``, script classes) keep GDScript's | ||
| existing nullability. Unlike built-in value types, an object type accepts ``null`` | ||
| at runtime whether or not it is marked with ``?``, so a null object reaching a | ||
| non-nullable object type is **not** a runtime error. | ||
|
|
||
| What ``?`` adds for objects is static tracking. ``Node?`` participates in | ||
| null-narrowing and raises ``UNSAFE_NULLABLE_ACCESS`` when the value is transferred | ||
| to a non-nullable target (assigned, returned, or passed as an argument). Accessing | ||
| a member or calling a method directly on a nullable object does *not* currently | ||
| raise the warning; only built-in value types are checked for direct use. |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 'null instance|Attempt to call|UNSAFE_NULLABLE_ACCESS|Node\?' --glob '*.rst' --glob '*.cpp' --glob '*.h' .Repository: Redot-Engine/redot-docs
Length of output: 7209
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- nullable-types.rst ---'
sed -n '130,215p' tutorials/scripting/gdscript/gdscript_nullable_types.rst
printf '%s\n' '--- null-instance documentation ---'
sed -n '245,265p' getting_started/first_2d_game/03.coding_the_player.rst
sed -n '2125,2135p' classes/class_node.rst
printf '%s\n' '--- repository scope ---'
git ls-files | rg '(^|/)(gdscript_nullable_types|gdscript|script|variant|object|node)' | head -120Repository: Redot-Engine/redot-docs
Length of output: 12776
🌐 Web query:
Godot GDScript nullable object Node? null instance method call runtime official documentation
💡 Result:
In GDScript, calling a method on a null object or a previously freed instance results in a runtime error [1][2]. Because GDScript is dynamically typed and objects are nullable, attempting to access properties or call functions on a null reference will cause the engine to halt execution with an error such as "Invalid call. Nonexistent function" or "Cannot call method on a null value" [1][2]. To safely handle potentially null objects, you should implement the following patterns: 1. Use the Safe Call Operator (?.) The null-safe call operator (introduced in Godot 4) allows you to call a method or access a property only if the instance is not null [2]. If the instance is null, the expression short-circuits and returns null instead of throwing an error [3][2]. Example: node?.my_method() 2. Use is_instance_valid Before calling methods on an object that may have been freed or is potentially null, use the global function is_instance_valid [2]. This is the standard practice for checking if an object still exists in memory [2]. Example: if is_instance_valid(node): node.my_method 3. Avoid common pitfalls - @onready: Ensure node references are initialized correctly, often using the @onready annotation to avoid accessing nodes before they are added to the scene tree [1][4]. - Lifecycle management: Be cautious when using queue_free or free, as references to those objects immediately become invalid [4][2]. - Pathing: Verify that get_node paths are correct; if a path is invalid, it returns null, which will crash your code if accessed directly [1][5]. While some languages or proposed features may allow more graceful handling of nulls, GDScript requires these explicit checks to maintain safety at runtime [6][3].
Citations:
- 1: https://www.gdquest.com/library/null_instance_error/
- 2: https://bugnet.io/blog/godot-stack-trace-debugging-guide
- 3: [Draft] Add support for nullable types in GDScript godotengine/godot#76843
- 4: https://docs.godotengine.org/en/stable/tutorials/scripting/nodes_and_scene_instances.html
- 5: https://forum.godotengine.org/t/cannot-call-method-get-node-on-a-null-value/117531
- 6: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript%5Fadvanced.html
🌐 Web query:
site:docs.godotengine.org/en/stable GDScript null instance method call null object is_instance_valid
💡 Result:
In GDScript, calling a method on a null instance will trigger a runtime error [1]. It is essential to ensure an object is valid before attempting to access its members or methods [1]. When working with Godot Objects (which include Nodes), a variable may contain a reference to an object that has been freed from memory, even if the variable itself is not null [1]. Therefore, checking against null (e.g., if my_node!= null) is insufficient for verifying if an object still exists [1]. To safely check if an object instance is still valid, use the built-in function is_instance_valid [1][2]: if is_instance_valid(my_node): my_node.my_method Additionally, if you are unsure whether an object has a specific method, you can use the has_method function to prevent runtime errors [3][1]: if is_instance_valid(my_node) and my_node.has_method("my_method"): my_node.my_method For objects that inherit from RefCounted, the memory is managed automatically and the reference will typically become null when the object is no longer in use, but is_instance_valid remains a robust way to verify any Object-derived instance [3][1].
Citations:
- 1: https://docs.godotengine.org/en/stable/classes/class_object.html
- 2: https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html
- 3: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript%5Fadvanced.html
Document runtime failures for direct nullable-object access.
Although Node? member and method access does not emit UNSAFE_NULLABLE_ACCESS, access can still fail at runtime when the reference is null. State this explicitly and add a narrowed example:
if node != null:
node.some_method()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tutorials/scripting/gdscript/gdscript_nullable_types.rst` around lines 188 -
200, Update the “Notes on objects and enums” section to explicitly state that
direct member access or method calls on a null nullable-object reference can
still fail at runtime, despite no warning being emitted. Add a narrowed GDScript
example using `if node != null:` before calling `node.some_method()`.
| Freed objects keep GDScript's existing semantics. After ``node.free()``, comparing | ||
| ``node == null`` returns ``true``, while ``is_instance_valid(node)`` returns | ||
| ``false`` — the reference still points at the freed instance but compares equal to | ||
| ``null``. |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 'is_instance_valid|freed instance|previously freed|== null' --glob '*.rst' --glob '*.cpp' --glob '*.h' .Repository: Redot-Engine/redot-docs
Length of output: 25423
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- nullable tutorial context ---'
sed -n '180,215p' tutorials/scripting/gdscript/gdscript_nullable_types.rst
printf '%s\n' '--- Object validity documentation ---'
sed -n '40,55p;1038,1047p' classes/class_object.rst
printf '%s\n' '--- is_instance_valid documentation ---'
sed -n '6128,6136p' classes/class_@globalscope.rst
printf '%s\n' '--- repository references to freed-object equality ---'
rg -n -C 3 'freed objects|freed instance|become invalid|equal to ``null``|compare.*null|is_instance_valid' \
--glob '*.rst' --glob '*.md' --glob '*.cpp' --glob '*.h' .Repository: Redot-Engine/redot-docs
Length of output: 19084
Use is_instance_valid() as the freed-object check.
The tutorial contradicts the Object documentation. A freed object reference can remain non-null, so node == null must not be presented as a general freed-object check. State that is_instance_valid(node) is required to determine whether the object still exists.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tutorials/scripting/gdscript/gdscript_nullable_types.rst` around lines 207 -
210, Update the freed-object explanation near the nullable GDScript example to
state that node == null is not a general freed-object check because a freed
reference may remain non-null. Identify is_instance_valid(node) as the required
check for determining whether the object still exists, and remove the
contradictory claim that node == null reliably detects freed objects.
Tandem to Redot-Engine/redot-engine#1335
Summary by CodeRabbit