Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is exactly the fix proposed in #660 by @kidrigger in order to fix #652. The original PR was closed with the code removed somehow, but I believe it is a proper fix as discussed below. Another probably related PR #662 also removed the original code (only test cases left) after the merge of godotengine/godot#57968, which however didn't solve all the problems.
To summarize, when a function news and returns a new ref counted object like the code below, the object is freed immediately after the function returns so on the GDScript side we only see a null object return.
The reason is that
PtrToArgonly passes the raw pointer to the engine so the moment theRefgets destroyed it frees the object.https://github.com/zhehangd/godot-cpp/blob/master/include/godot_cpp/classes/ref.hpp#L249
The solution proposed by #660 well fixed this issue.
@BastiaanOlij mentioned a concern that this may overwrite an existing pointer.
The discussion happened several months ago and I don't the circumstance at that time, but for what I see in the current codebase this is not an issue.
PtrToArg::encodeis used to supportMethodBind::bind_ptrcall. On the engine side this is called from https://github.com/godotengine/godot/blob/b7346e50258655316a4541d17fd92cc3b3a3f6ef/modules/gdscript/gdscript_vm.cpp#L1907VariantInternal::initializeguarantees that the object pointer is cleared.In addition, I think it is always caller's duty to ensure that the dst pointer is ready to take value.
Here is a C++ snippet to test this fix
There are four functions that create a new RefCounted in different ways.
The first function returns a raw pointer without using Ref at all.
The second function is the standard way to create an object, which does not work without the fix.
The third function uses a workaround by giving it an extra reference, but this causes new troubles if other C++ functions also use it.
The fourth function converts the
Refto aVariantas return, this always works as Variant preserves the ownership inside the engine.GDScript
Here is the result for Alpha13 without the fix
is_validmeans the function returns a non-null object,is_freedmeans the object is freed after all references are lost.Clearly, without the workaround the object cannot return properly.
Somehow with the workaround the object leaks, which I don't really understand (didn't investigate, but the hacked extra reference should have be given to the engine variant), but anyway that is not the point of this PR.
This is if we apply the fix. Everything works fine. Case 3 leaks the object as expected as we have extra reference.
So that is all about this fix.
Another thing I am thinking is about is the first test case when we directly return a raw pointer.
It works almost the same as returning a
Ref, with one subtle difference that no one callsRefCounted::init_ref(not very sure).As far as I know only the constructors and assignments of
VariantandRefcallinit_refwhileptrcalldoes not go through any of them.This may expose some risks, but I don't really know about it.
So I suggest that we either fix this inconsistency or disallow returning RefCount by raw pointer. That would be another PR.