Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix expected argument count for Callable call errors #80844

Merged
merged 1 commit into from
Oct 2, 2023

Conversation

dalexeev
Copy link
Member

@dalexeev dalexeev commented Aug 21, 2023

struct CallError {
enum Error {
CALL_OK,
CALL_ERROR_INVALID_METHOD,
CALL_ERROR_INVALID_ARGUMENT, // expected is variant type
CALL_ERROR_TOO_MANY_ARGUMENTS, // expected is number of arguments
CALL_ERROR_TOO_FEW_ARGUMENTS, // expected is number of arguments
CALL_ERROR_INSTANCE_IS_NULL,
CALL_ERROR_METHOD_NOT_CONST,
};
Error error = Error::CALL_OK;
int argument = 0;
int expected = 0;
};

@akien-mga
Copy link
Member

From a cursory look this seems sensible, but I'm surprised that it was wrong in so many places in the codebase. Is that due to an API design change that wasn't properly propagated?

@dalexeev
Copy link
Member Author

dalexeev commented Aug 21, 2023

Is that due to an API design change that wasn't properly propagated?

I think argument was the right property before. Then, after the refactoring, the code was not updated in some places, and perhaps bad cases increased due to copy-paste without looking into the header. See diff.

Error error;
int argument;
Type expected;

See also #76259. The problem is that #36368 declared expected as a property responsible for this (diff), but until #76259, argument was actually responsible for this (diff).

core/object/object.cpp Outdated Show resolved Hide resolved
core/variant/variant_utility.cpp Outdated Show resolved Hide resolved
core/variant/variant_utility.cpp Outdated Show resolved Hide resolved
platform/android/java_class_wrapper.cpp Outdated Show resolved Hide resolved
@dalexeev
Copy link
Member Author

dalexeev commented Sep 13, 2023

Perhaps the first mistake was in the comment due to bad copy-paste and the following was intended:

 	enum Error {
 		CALL_OK,
 		CALL_ERROR_INVALID_METHOD,
 		CALL_ERROR_INVALID_ARGUMENT, // expected is variant type
- 		CALL_ERROR_TOO_MANY_ARGUMENTS, // expected is number of arguments
- 		CALL_ERROR_TOO_FEW_ARGUMENTS, // expected is number of arguments
+ 		CALL_ERROR_TOO_MANY_ARGUMENTS, // argument is number of arguments 
+ 		CALL_ERROR_TOO_FEW_ARGUMENTS, // argument is number of arguments
 		CALL_ERROR_INSTANCE_IS_NULL,
 		CALL_ERROR_METHOD_NOT_CONST,
 	};
 	Error error = Error::CALL_OK;
 	int argument = 0;
- 	int expected = 0;
+ 	Variant::Type expected = Variant::NIL;

And then we started changing argument to expected in the code to match the comment.

In my opinion, the original version (like in 3.x) makes more sense, since the argument property is always responsible for the argument count/index. Now in #81605 we have to use switch:

switch (r_call_error.error) {
    case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
        r_call_error.argument -= captures_amount;
        // ...
        break;
    case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
    case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
        r_call_error.expected -= captures_amount;
        // ...
        break;
    default:
        break;
}

I'm not sure if CallError is an internal API or if it's also exposed to GDExtension.

@akien-mga
Copy link
Member

I'm not sure if CallError is an internal API or if it's also exposed to GDExtension.

GDExtension has a similar struct (and the comments are even more wrong as they were capitalized :)).

typedef enum {
	GDEXTENSION_CALL_OK,
	GDEXTENSION_CALL_ERROR_INVALID_METHOD,
	GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT, // Expected a different variant type.
	GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS, // Expected lower number of arguments.
	GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS, // Expected higher number of arguments.
	GDEXTENSION_CALL_ERROR_INSTANCE_IS_NULL,
	GDEXTENSION_CALL_ERROR_METHOD_NOT_CONST, // Used for const call.
} GDExtensionCallErrorType;

typedef struct {
	GDExtensionCallErrorType error;
	int32_t argument;
	int32_t expected;
} GDExtensionCallError;

If we need to change the meaning of argument/expected, maybe this might need to be done via a new GDExtensionCallError2 struct, preserving the compatibility for GDExtensionCallError uses somewhat. CC @dsnopek

@dsnopek
Copy link
Contributor

dsnopek commented Sep 29, 2023

If we need to change the meaning of argument/expected, maybe this might need to be done via a new GDExtensionCallError2 struct, preserving the compatibility for GDExtensionCallError uses somewhat

Hrm. If we wanted to make a new GDExtensionCallError2, then we'd need to also make new versions of every function that deals with GDExtensionCallError, which includes some function pointers that are used on other structs, which would need new versions along with all the functions that accept those too. This quickly becomes quite a few changes.

Am I correct in understanding that this just affects the accuracy of some error messages? If so, the worst that could happen, is that a GDExtension compiled for Godot 4.1 but opened in Godot 4.2 could generate some incorrect error messages in some cases. This could maybe just be an acceptable weirdness with using older GDExtensions? It's not a crash, and it's not an error interpreted as a success or vice-versa.

We should also probably make changes similar to those in this PR to godot-cpp, so that it's using argument and expected correctly too.

@ParadoxV5
Copy link

If we need to change the meaning of argument/expected, maybe this might need to be done via a new GDExtensionCallError2 struct, preserving the compatibility for GDExtensionCallError uses somewhat.

Am I correct in understanding that this just affects the accuracy of some error messages? […] This could maybe just be an acceptable weirdness with using older GDExtensions? It's not a crash, and it's not an error interpreted as a success or vice-versa.

We should also probably make changes similar to those in this PR to godot-cpp, so that it's using argument and expected correctly too.

Here’s what this GDExtension user thinks. I say no needed, because

Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

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

The PR has been updated to fix the bug without breaking compat, so I think it's good to go.
Similar fixes might be needed in godot-cpp.

@akien-mga akien-mga merged commit e95b7e8 into godotengine:master Oct 2, 2023
15 checks passed
@akien-mga
Copy link
Member

Thanks!

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

Successfully merging this pull request may close these issues.

Strange error message: "Method expected 0 arguments, but called with 0"
5 participants