Skip to content

nullable static types documentation requested. - #175

Closed
mcdubhghlas wants to merge 1 commit into
Redot-Engine:masterfrom
mcdubhghlas:nullable-types-docs
Closed

nullable static types documentation requested.#175
mcdubhghlas wants to merge 1 commit into
Redot-Engine:masterfrom
mcdubhghlas:nullable-types-docs

Conversation

@mcdubhghlas

@mcdubhghlas mcdubhghlas commented Aug 11, 2026

Copy link
Copy Markdown
Member

Tandem to Redot-Engine/redot-engine#1335

Summary by CodeRabbit

  • Documentation
    • Added a comprehensive guide to nullable types in GDScript.
    • Documented nullable syntax, declarations, null narrowing, runtime enforcement, typed containers, and object and enum behavior.
    • Added the new guide to the GDScript documentation index.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Nullable types documentation

Layer / File(s) Summary
Nullable syntax and supported types
tutorials/scripting/gdscript/gdscript_nullable_types.rst, tutorials/scripting/gdscript/index.rst
The tutorial documents ? syntax, nullable declarations, analyzer behavior, supported types, rejected forms, and typed-container rules. The index adds the tutorial to the table of contents.
Narrowing and access warnings
tutorials/scripting/gdscript/gdscript_nullable_types.rst
The tutorial describes null narrowing in guards, branches, loops, boolean conditions, and reassignment. It also documents UNSAFE_NULLABLE_ACCESS, warning configuration, and suppression.
Runtime and object behavior
tutorials/scripting/gdscript/gdscript_nullable_types.rst
The tutorial describes runtime rejection of null for built-in value and enum slots, typed-container checks, and object and enum nullability behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: documentation for nullable static types.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 099c9f8 and 56eeef0.

📒 Files selected for processing (2)
  • tutorials/scripting/gdscript/gdscript_nullable_types.rst
  • tutorials/scripting/gdscript/index.rst

Comment on lines +6 to +16
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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.rst

Repository: 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.

Comment on lines +20 to +24
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.rst

Repository: 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')]}")
PY

Repository: 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.

Comment on lines +188 to +200
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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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 -120

Repository: 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:


🌐 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:


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()`.

Comment on lines +207 to +210
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``.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant