Skip to content

Empty variadic parameters of functions called in cascade will accumulate empty array elements #112832

@Klaim

Description

@Klaim

Tested versions

Godot v4.5-stable

System information

Godot v4.5.1.stable - Windows 11 (build 26100) - Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3080 Ti (NVIDIA; 32.0.15.8115) - AMD Ryzen 9 5950X 16-Core Processor (32 threads) - 95.92 GiB memory

Issue description

When passing no arguments into a variadic function, and then in that function passing these variadic parameters into another variadic function, this creates an array element instead of passing the values to "spread" as the doc calls it.

Image

In particular, this will make code ending up calling callv completely fail for functions without arguments:

static func call_if_exists(node: Node, func_name: StringName, ...args):
	print("node: %s  call : %s " % [ node.name, func_name])
	if node and node.has_method(func_name):
		node.callv(func_name, args)

static func call_any_if_exists(nodes: Array[Node], func_name: StringName, ...args):
	for node in nodes:
		call_if_exists(node, func_name, args)

With the code above, calling this:

call_any_if_exists(some_nodes, "some_function") # no additional arguments

will trigger an error when callv in above code snippet is called, making variadic arguments almost impossible to use generically.

In my own project, using that same code, the error looks like this:

E 0:00:06:138   utility.gd:10 @ call_if_exists(): Error calling method from 'callv': 'CharacterBody3D(player.gd)::on_post_restore_begin': Method expected 0 argument(s), but called with 1.
  <C++ Error>   Method/function failed. Returning: Variant()
  <C++ Source>  core/object/object.cpp:812 @ callv()
  <Stack Trace> utility.gd:10 @ call_if_exists()
                utility.gd:14 @ call_any_if_exists()
                game_core.gd:473 @ end_game_load()
                game_scene.gd:61 @ restore_save()
                screen_loading_game.gd:85 @ _check_loading_status()
                screen_loading_game.gd:58 @ _process()

Using the debugger to break at this point and checking the value of args in each function, I can see that each call will add an inner array element.

Note that the documentation suggests to call callv to simulate a spread, but that will not work if the parameters come from several function upper in the stack:

Image

Steps to reproduce

In any GDScript, given this code:

func func_a(...args) -> void:
	func_b(args)
	
func func_b(...args) -> void:
	assert(args.is_empty())

func some_test() -> void:
	func_a();

Call some_test() to trigger the assertion and see the content of args in the debugger.

Obviously, the more calls you chain that way, the more inner arrays you will find, as per the above screenshot.

Minimal reproduction project (MRP)

N/A , see snippet above

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions