Repository: godot
Issue: Callable obtained from classes (static and non-static methods) have suspect and inconsistent behaviors in user defined & native classes
Labels: bug,topic:gdscript

### Tested versions

- Reproducible in `4.2.1.stable`, `4.2.stable`
- `4.2.1.stable` outputs error message `Attempt to call function 'null::<function name> (Callable)' on a null instance.` and `4.2.stable` has no outputs on it.

### System information

Godot v4.2.1.stable - macOS 14.3.1 - Vulkan (Forward+) - integrated Apple M2 Max - Apple M2 Max (12 Threads)

### Issue description

Callable instances of a static function, retrieved directly from the class, have strange & inconsistent behaviors.


## Classes Used in Test

Suppose a user defined (inner or global, by `class` or `class_name` will have the same behavior regarding this issue) class is claimed as the following, and then `MyClass` should be an instance of `GDScript`:

```gdscript
class MyClass:
	static func my_static() -> void:
		print("success")
```

And the built-in classes as `GDScriptNativeClass` instances also have many static functions, take class `Node` and its static function `print_orphan_nodes()` as an example. 


## Behavior Observation and Conclusion

Details of the testing results please see the steps section.

The callable instance of functions `print_orphan_nodes` and `my_static` will have the following behaviors:

### User Classes
- For user defined classes, using a **string name** to retrieve the method directly from the class (instead of an instance) will get a callable on `null` object.
	- Callables obtained by `MyClass.my_static` and `my_instance.my_static` work normally.
	- Callables obtained by `my_instance["my_static"]` and `Callable(my_instance, "my_static")` work normally.
	- Callables obtained by `MyClass["my_static"]` and `Callable(MyClass, "my_static")` will raise error `Attempt to call function 'null::my_static (Callable)' on a null instance.`.
		- However, explicitly **casting** `MyClass` as any of its ancestor built-in classes' instance (`GDScript`, `Script`, ..., `Object`) will allow the string name to work, like `MyClass as GDScript`.

### Native Classes
- For built-in classes, the callables obtained from an instance still works in all the cases, but from the class itself the behaviors get even more confusing:
	- Static methods are even **not considered as a member** of the class itself, checking with the `in` operator.
		- Thus, you cannot obtain any callable (even not like in the user defined classes, an invalid/unusable callable `null::my_static`) by `Node.print_orphan_nodes`, `Node["print_orphan_nodes"]`, `(Node as Object).print_orphan_nodes`, `(Node as Object)["print_orphan_nodes"]`.
	- However, you can find and successfully call directly by `Node.print_orphan_nodes()` (how weird given that `Node.print_orphan_nodes` doesn't even exist!)
	- And strangely, you can create callable instance `Callable(Node, "print_orphan_nodes")` and call it with no problem.

### Moreover, for non-static methods on `Object`...

Might be related to this #77567 issue. Since those classes are instances of `GDScript` and `GDScriptNativeClass`, and therefore instances of `Object`, they should have access to `Object`'s instance method, taking `get_method_list` as an example here.

I tested on user and native classes and their behavior also have discrepancy.

- In user classes and native classes, `get_method_list` is confirmed to be a member of both the classes themselves.
- In user classes, 
	- For string name retrived callables, the behavior is mostly the same as static method defined on the user class: callables on `null` object, and can be fixed by casting.
	- Noticable difference: 
		- Directly calling `MyClass.get_method_list()` raises a parse error (since the parser thinks it's not a static function and cannot be called on a class)
		- But geting the callable `MyClass.get_method_list` first and then `.call()` on it will successfully execute.
		- Or, it can be fixed by casting and reminding the parser `MyClass` is an `Object`: `(MyClass as Object).get_method_list()` will work.
	- Therefore, there are some ways to obtain valid callables: 
		- `MyClass.get_method_list`
		- `(MyClass as Object).get_method_list`
		- `(MyClass as Object)["get_method_list"]`
		- `Callable(MyClass as Object, "get_method_list")`
- In native classes,
	- All the aforementioned ways will be able to obtain a seemingly valid callable `GDScriptNativeClass::get_method_list`, but none of them are really "callable". Calling them will all generate `Nonexistent function` error.
	- Compared to user classes:
		- Directly calling `Node.get_method_list()` also raises Parse Error as the same.
		- However, not like in user classes, `Node.get_method_list.call()` won't fix it, as mentioned before, it is a fake callable on nonexistent function.
		- The only way to run the function immediately is by casting: `(Node as Object).get_method_list()`
	- But I don't see any way to produce a valid callable for storing and passing the function reference. Not like in user classes. All will raise nonexistent function error.
		- It is strange given that `(Node as Object).get_method_list()` can run, but `(Node as Object).get_method_list.call()` is nonexistent.


### Steps to reproduce

## User Classes

```gdscript
@tool
extends EditorScript

func _run() -> void:
	var my_instance := MyClass.new()
	
	print(MyClass) # <GDScript#-9223365398814983298>
	print(my_instance) # <RefCounted#-9223364908316299205>
	
	
	# ------------- Direct calls from identifier -------------
	MyClass.my_static() # success	
	my_instance.my_static() # success (with warning)
	
	# ------------- Callables from identifier -------------	
	# From class
	var callable_from_class := MyClass.my_static
	print(callable_from_class) # GDScript::my_static
	callable_from_class.call() # success
	
	# From instance
	var callable_from_instance := my_instance.my_static
	print(callable_from_instance) # RefCounted::my_static
	callable_from_instance.call() # success (with no warning)
	
	
	# ------------- Callables from string name -------------	
	# Check if MyClass and my_instance has member "my_static"
	print("my_static" in MyClass) # true
	print("my_static" in my_instance) # true
	
	
	# ------------- Callables from index -------------	
	var callable_from_index := MyClass["my_static"]
	print(callable_from_index) # null::my_static
	# error: Attempt to call function 'null::my_static (Callable)' on a null instance.
	# callable_from_index.call() 
	
	callable_from_index = my_instance["my_static"]
	print(callable_from_index) # RefCounted::my_static
	callable_from_index.call() # success (with no warning)
	
	
	# ------------- Callables from constructor -------------	
	var callable_from_constructor := Callable(MyClass, "my_static")
	print(callable_from_constructor) # null::my_static
	# error: Attempt to call function 'null::my_static (Callable)' on a null instance.
	# callable_from_constructor.call() 
	
	callable_from_object = Callable(my_instance, "my_static")
	print(callable_from_object) # RefCounted::my_static
	callable_from_object.call() # success (with no warning)
	
	
	# ------------- Callables from cast class -------------	
	# Store the cast class,
	# or using (MyClass as GDScript) in place will work as the same.
	# Do not have to be GDScript,
	# All builtin ancestor classes can work, such as Object.
	var cast_class: GDScript = MyClass 
	
	var callable_from_cast_class: Callable = cast_class["my_static"]
	print(callable_from_cast_class) # GDScript::my_static
	callable_from_cast_class.call() # success
	
	callable_from_cast_class = (MyClass as Object)["my_static"]
	print(callable_from_cast_class) # GDScript::my_static <- will still be GDScript instance
	callable_from_cast_class.call() # success
	
	
	# Creating callables using the cast class as its object will also work.
	callable_from_cast_class = Callable(cast_class, "my_static")
	print(callable_from_cast_class) # GDScript::my_static
	callable_from_cast_class.call() # success
```

## Native Classes
```gdscript
@tool
extends EditorScript

func _run() -> void:
	var node_instance := Node.new()
	
	print(Node) # <GDScriptNativeClass#-9223372023818878356>
	print(node_instance) # <Node#20613024514636>
	
	
	# ------------- Direct calls from identifier -------------
	Node.print_orphan_nodes() # success	
	node_instance.print_orphan_nodes() # success (with warning)
	
	# ------------- Callables from identifier -------------	
	# Cannot find from class
	# error: Invalid get index 'print_orphan_nodes' (on base: 'Node').
	#var callable_from_class := Node.print_orphan_nodes
	
	# From instance
	var callable_from_instance := node_instance.print_orphan_nodes
	print(callable_from_instance) # Node::print_orphan_nodes
	callable_from_instance.call() # success (with no warning)
	
	
	# ------------- Callables from string name -------------	
	# Check if Node and node_instance has member "print_orphan_nodes"
	print("print_orphan_nodes" in Node) # false, but why?
	print("print_orphan_nodes" in node_instance) # true
	
	
	# ------------- Callables from index -------------	
	# error: Invalid get index 'print_orphan_nodes' (on base: 'Node').
	#var callable_from_index: Callable = Node["print_orphan_nodes"]

	
	var callable_from_index: Callable = node_instance["print_orphan_nodes"]
	print(callable_from_index) # Node::print_orphan_nodes
	callable_from_index.call() # success (with no warning)
	
	
	# ------------- Callables from constructor -------------	
	var callable_from_constructor := Callable(Node, "print_orphan_nodes")
	print(callable_from_constructor) # GDScriptNativeClass::print_orphan_nodes
	callable_from_constructor.call() # success!
	
	callable_from_object = Callable(node_instance, "print_orphan_nodes")
	print(callable_from_object) # Node::print_orphan_nodes
	callable_from_object.call() # success (with no warning)
	
	
	# ------------- Callables from cast class -------------	
	# Store the cast class,
	var cast_class: Object = Node 
	
	# Still cannot find from class
	#var callable_from_cast_class: Callable = cast_class["print_orphan_nodes"]
	#var callable_from_cast_class: Callable = cast_class.print_orphan_nodes
	
	# Only creating callable instance from cast class works the same
	var callable_from_cast_class := Callable(cast_class, "print_orphan_nodes")
	print(callable_from_cast_class) # GDScriptNativeClass::print_orphan_nodes
	callable_from_cast_class.call() # success
```

## Object Instance Method

```gdscript
	# Directly call from MyClass or Node will not succeed, 
	# even when they are instances of Object
	print(MyClass is Object) # true
	print(Node is Object) # true
	# Parse Error: Cannot call non-static function "get_method_list()" 
	# on the class "MyClass" directly. Make an instance instead.
	#MyClass.get_method_list() # error
	#Node.get_method_list() # error
	
	# Checking if the classes have the member
	print("get_method_list" in MyClass) # true
	print("get_method_list" in Node) # true
	
	var test: Callable
	
	# Trying to call the two callables below will raise error:
	# Attempt to call function 'null::get_method_list (Callable)' on a null instance.
	test = MyClass["get_method_list"] # null::get_method_list
	test = Callable(MyClass, "get_method_list") # null::get_method_list
	# And the following three will succeed
	test = MyClass.get_method_list # GDScript::get_method_list
	test = (MyClass as Object).get_method_list # GDScript::get_method_list
	test = (MyClass as Object)["get_method_list"] # GDScript::get_method_list
	test = Callable(MyClass as Object, "get_method_list") # GDScript::get_method_list
	
	# Trying to call all the callables below will raise error:
	# Invalid call. Nonexistent function 'GDScriptNativeClass::get_method_list (Callable)'.
	test = Node["get_method_list"] # GDScriptNativeClass::get_method_list
	test = Callable(Node, "get_method_list") # GDScriptNativeClass::get_method_list
	test = Node.get_method_list # GDScriptNativeClass::get_method_list
	test = (Node as Object).get_method_list # GDScriptNativeClass::get_method_list
	test = (Node as Object)["get_method_list"] # GDScriptNativeClass::get_method_list
	test = Callable(Node as Object, "get_method_list")
	
	# Explicitly casting as Object will allow calling 
	(MyClass as Object).get_method_list() # success
	(Node as Object).get_method_list() # success
```

### Minimal reproduction project (MRP)

N/A