### Godot version

4.0 beta 10, also 3.5.1

### System information

Windows 11

### Issue description

Creating a Mainloop inherited from SceneTree makes sense when you want to add a few features to SceneTree instead of starting from scratch. Unfortunately, this doesn't work as well, as if its type is erased.

But inheriting from Mainloop directly (in the example) can work properly.


### Steps to reproduce

1. Create a mainloop extends SceneTree. In this case, I call it `MyGDScriptMainLoop`.

```gdscript
class_name MyGDScriptMainLoop extends SceneTree

func say_hello():
    print("hello")
```

2. Go to project settings, set `application/run/main_loop_type` to `MyGDScriptMainLoop`.
3. Create a test scene and try to call `Engine.get_main_loop().say_hello()`.

```gdscript
extends Node2D


# Called when the node enters the scene tree for the first time.
func _ready() -> void:
    Engine.get_main_loop().say_hello()
    pass # Replace with function body.
```

Then you will see:

![image](https://user-images.githubusercontent.com/93276898/210129049-0250a15f-2f87-49a1-b066-16ef5e695302.png)

And also I found that `Engine.get_main_loop() is MyGDScriptMainLoop` is `false`.

Using GDExtension to do this also failed. But since I'm using godot-rust, I'm not sure if this problem exists with godot-cpp.

### Minimal reproduction project

[bug.zip](https://github.com/godotengine/godot/files/10327002/bug.zip)

*
 * Consists of several methods that are called by each platform's specific main(argc, argv).
 * To fully understand engine init, one should therefore start from the platform's main and
 * see how it calls into the Main class' methods.
 *
 * The initialization is typically done in 3 steps (with the setup2 step triggered either
 * automatically by setup, or manually in the platform's main).
 *
 * - setup(execpath, argc, argv, p_second_phase) is the main entry point for all platforms,
 *   responsible for the initialization of all low level singletons and core types, and parsing
 *   command line arguments to configure things accordingly.
 *   If p_second_phase is true, it will chain into setup2() (default behavior). This is
 *   disabled on some platforms (Android, iOS, UWP) which trigger the second step in their
 *   own time.
 *
 * - setup2(p_main_tid_override) registers high level servers and singletons, displays the
 *   boot splash, then registers higher level types (scene, editor, etc.).
 *
 * - start() is the last step and that's where command line tools can run, or the main loop
 *   can be created eventually and the project settings put into action. That's also where
 *   the editor node is created, if relevant.
 *   start() does it own argument parsing for a subset of the command line arguments described
 *   in help, it's a bit messy and should be globalized with the setup() parsing somehow.
 */
Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) {
	Thread::make_main_thread();
	set_current_thread_safe_for_nodes(true);
	OS::get_singleton()->initialize();