-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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
UNSAFE_CAST
warning is useless or has confusing description
#94918
Comments
CC @dalexeev |
This is not a regression. The warning has been adjusted to reflect the actual behavior of the Currently you can check the type using the var a: Variant
if a is int:
var b: int = a Or alternatively you can disable the warning in the Project Settings if you don't care about this behavior.
This is not an implicit type casting, but a type narrowing. There is a proposal for an explicit type narrowing syntax, but we tend to think that the static analyzer should do a better job of tracking control flow in general and local assignments and type checks in particular.
This is correct if you have already checked the type before. In this case, type casting ( |
@dalexeev Ah, just as you were commenting I had updated the issue with a newer realization:
My understanding is that my referring to In any case, now that I realize that ignoring UNSAFE_CAST does not affect anything except for Variants and it was not providing any benefit before or after #84043, I can just turn off that error/warning. I had assumed that the "Invalid cast" errors for problematic casts with other non-Variant types were affected by that setting as well, but it turns out not. This still seems like a problem though. |
@pineapplemachine Please take a look at the following example to better understand the warning purpose: var a: CanvasItem = Node2D.new()
# Compile time: type safe.
# Runtime: OK.
var b := a as Control
print(b) # Prints "<null>"
var c: Variant = 123
# Compile time: UNSAFE_CAST warning.
# Runtime: Error "Invalid cast: could not convert value to 'String'.".
var d := c as String
print(d) There is the same type relationship between Objects are nullable, |
This code produces a runtime error, because it contains an invalid implicit cast. But it does not trigger any warning or error currently: var variant_value: Variant = ""
var int_value: int = variant_value # Trying to assign value of type 'String' to a variable of type 'int' This code produces an UNSAFE_CAST error, though it does not cause a runtime error: var variant_value: Variant = Node.new()
var control: Control = variant_value as Control # Casting "Variant" to "Control" is unsafe.
print(control) # Prints <null> (No runtime error!) Here is what should happen eventually:
But right now, neither nullable value types nor type narrowing via type guards are supported by GDScript. Because of that, the UNSAFE_CAST error is useless. This is because there is no way to cast a Variant to a value type that does not violate GDScript's type safety, except through the loophole of using an implicit cast (via either assignment or returning from a function with a return type annotation), since implicit casts are not yet checked by the type safety system. The new 4.3 behavior doesn't even permit explicit casting in cases that can be statically verified not to produce a runtime error, i.e. the case of casting to a nullable reference type. |
var variant_value: Variant = 123
# Compile time: UNSAFE_CAST warning.
# Runtime: Error "Invalid cast: can't convert a non-object value to an object type.".
var control: Control = variant_value as Control
print(control)
Please read my comments carefully. The fact that the It does not matter that you assume that
Please look at the test cases in #84043 and study the issue carefully before suggesting solutions. |
I believe I do understand what you are saying. I think you have missed my point. The error is useless because:
The result is that there is no reason for anyone to turn on UNSAFE_CAST as a warning or error in their project. The error/warning conveys no useful information, not in 4.2 or in 4.3, because the only way to comply and silence the warning/error is via the implicit cast loophole in the type system, where explicit casts are checked for type safety but implicit casts are not. |
Further attempting to explain myself... For the warning/error to not be useless, there would need to be an explicit way to unbox a value type from a Variant that will not cause either a runtime error or a type safety error. Such as via support for nullable value types and/or support for type narrowing, which I explained before. Typed convert functions that do not themselves return another Variant, like I previously suggested with Also, the error should not occur when casting to a reference type, e.g. If these two issues were resolved: 1. Lack of an explicit way to cast or convert value types without triggering the error/warning and 2. Lack of an explicit way to cast reference types without triggering the error/warning, then UNSAFE_CAST would no longer be useless, and there would be a constructive reason for users to set UNSAFE_CAST to produce an error or warning instead of being ignored. Additionally, implicit casts (assignments and returning from a function with a return type annotation) should really be checked with at least the same strictness as explicit casts using I am a hobbyist creator of small programming languages and a contributor in passing to tools like linters and standard libraries. I promise you I understand the problem. Please engage with my argument and do not dismiss my comments by suggesting I have not read carefully enough. |
This is not an implicit cast, but an implicit and unsafe type narrowing. The line is marked as unsafe, but there is no named warning, unlike other situations: Currently, it is your responsibility to ensure that this type narrowing is safe at runtime. Also, if you are trying to always have a green line instead of a gray one, please see the docs:
I was thinking about adding
No, I demonstrated this above. Casting a The fact that there is no explicit type narrowing syntax or that the static analyzer does not properly track the control flow is not directly related to this warning. The fact that in the future we may change the behavior of the The point of this warning is to inform you that casting |
This is not what the term "type narrowing" normally refers to. See, for example: https://mypy.readthedocs.io/en/stable/type_narrowing.html It is accurate to refer to this as implicit casting. This is because a value of one type is being converted (cast) to another, without the use of explicit conversion or casting semantics. https://medium.com/@tiwaridiwakar9/type-casting-implicit-vs-explicit-7e6fea5ba823 Here is another way to hopefully demonstrate this difference between type narrowing and implicit casting: The line is still marked as unsafe even if it is preceded by a type guard. If the GDScript compiler recognized type narrowing, then the assignment line would not be marked as unsafe. I think an UNSAFE_ASSIGNMENT warning would be a good idea.
Ok, now I see what you mean with this. Then this means that simply making
I agree. Since UNSAFE_CAST can be safely ignored, it probably is not appropriate to call this a regression as in the title of the issue. I was initially under the mistaken impression that the UNSAFE_CAST project setting also encompassed "Invalid cast" errors where Variants are not involved, due to the vague documentation of the "Unsafe Cast" option in project GDScript settings in 4.2. But it does make UNSAFE_CAST useless, in that it does not provide constructive information to the user. This equivalent of "you must not use And I really doubt I'm the only one who turned on the "Unsafe Cast" errors expecting them to be helpful in catching mistakes given the 4.2 description of the setting, and who is going to be caught off-guard when 4.3 causes correct code that previously did not produce type warnings or errors to suddenly have warnings/errors, without a clear way to deal with them. Users will likely think they need to change their code somehow to let the compiler recognize their code as safely making an explicit conversion, rather than thinking they need to use an implicit cast instead, or to set UNSAFE_CAST to ignored. This is presumably going to disrupt a lot of users' work with Godot, like it did mine, once 4.3 releases.
What you are questionably describing as "implicit type narrowing" still results in the assignment line being marked as unsafe. And the assignment is marked the same whether it is preceded by what could be statically recognized as a type guard (but isn't) or not. Thank you for pointing that out about subtly differently colored line numbers, by the way. I had not yet realized that the color of the line number could communicate secret unnamed and unexplained warnings. |
Terminology may vary, so let me clarify what I mean. The terms "type casting" and "type conversion" are often used interchangeably. Type conversions occur in various situations, both explicitly and implicitly. In this issue, by "type casting" I meant only the Type narrowing or downcasting is a special case of type conversion where the source type is converted to its subtype. As opposed to the general case where the source and target types may not be a supertype and subtype (like The # Option 1. Unsafe in fact and marked as unsafe.
var a: Variant = <Variant value>
var b := a as int # Correct UNSAFE_CAST warning. # Option 2. Safe in fact, but marked as unsafe.
var a: Variant = <Variant value>
if a is int:
var b1: int = a # Unsafe line (unsafe assignment).
var b2 := a as int # Unsafe line (UNSAFE_CAST). # Option 3. Safe in fact, but marked as unsafe.
var a: Variant = <Variant value>
if a is not int:
return
var b1: int = a # Unsafe line (unsafe assignment).
var b2 := a as int # Unsafe line (UNSAFE_CAST). Of course, this is not true at the moment and there is no actual difference between downcasting assignment and the type casting operator, in both cases GDScript double checks types at runtime (using different opcodes though). But there is a conceptual difference, in my opinion the
It's incorrect to compare
Yes, that's pretty much what the warning implies. If you strive for type-safe code, you shouldn't use the If you have a suggestion for improving the warning message, I would appreciate it. In my opinion, the warning is not useless. It tries to be as helpful as possible in this situation. Note that we originally had the |
variant_value as [Type]
) is always unsafe, type_convert
is not a substituteUNSAFE_CAST
warning is useless or has confusing description
I don't think that a superfluous
Gotcha. It may have been more appropriate to write
I think my primary suggestion would be to elaborate in the description of the project setting. So perhaps changing from:
To something like:
I think that would help to prevent confusion for users initially discovering the setting. But for users who already had the setting turned on without fully understanding its behavior, perhaps it could help to change the message from:
To something like:
But: If there is any room for small feature additions yet in 4.3, I think it would go a long way to add typed conversion functions that are like In any case, I made a proposal to more formally suggest this addition: godotengine/godot-proposals#10320 |
Ok, so I'm running into this issue as well. I must admit, the discussion above is way above my head. I am simply trying to cast from Variant to a known type. After checking with the is keyword that my variable is the correct type. How is that unsafe? I can absolutely understand that doing so without checking first is unsafe, but in prior versions, checking with the is keyword seemed to nullify this error, as it should be. |
@theDoktorJ I recommend turning off the "Unsafe Cast" warning by setting this option to "Ignore" in your project settings. To try to TL;DR it a bit: Right now the Unsafe Cast warning is of questionable helpfulness. The main reason for this is that it's not yet smart enough to avoid very significant false positives, reporting problems where a human programmer can see that there clearly aren't, like the example in your report #95267 where you were already checking for the correct type using If you really want to keep the warning on in project settings instead of setting it to Ignore, you can fix the warning message by removing the explicit cast in your example, i.e. by changing |
Thank you, that was extremely helpful. I will just disable it for the time being. |
Tested versions
I noticed this regression when opening my project (previously 4.2.2) in v4.3.rc1.mono.official [e343dbb]
System information
Windows 10 64-bit
Issue description
I have most of the GDScript-related settings turned to errors or warnings in my project, because when they behave reasonably I find these warnings and errors to be very helpful in catching my mistakes as soon as I write them instead of later on at runtime.
Recently I opened up my project in 4.3-rc1 and noticed some new UNSAFE_CAST errors that were not present with 4.2.2. In particular this is happening wherever I was using
some_variant as [Type]
. This happens with both classes (nullable) and with primitives/value types (not nullable).Here's an example of an affected function. The line
var point := intersection as Vector3
now produces the errorCasting "Variant" to "Vector3" is unsafe. (Warning treated as error.)
.It is possible to work around this newly occurring error by changing the line to an implicit cast instead of an explicit cast, like so:
This regression was apparently introduced by #84043, which closed #84027.
Discussion on the related issue suggests that casts like
variant_value as [Type]
should always be considered unsafe, but the only alternative offered is to usetype_convert
, which also cannot be used except via an implicit cast (i.e. with a variable assignment or as the typed return value of a function, not usingas
) becausetype_convert
itself returns a Variant. This means that result oftype_convert
(e.g.type_convert(intersection, TYPE_VECTOR3)
per the above example) also cannot be used type-safely except via, again, an implicit cast.I think there really needs to be a way to explicitly convert a Variant to another typed value that does not produce a type safety error. And at some point there should probably be an UNSAFE_IMPLICIT_CAST error so that these implicit casts can also be configured to generate errors.
Ideally I would like very much to see nullable types merged for 4.3 and for casts from Variant to any nullable type to be considered safe (producing null if the Variant did not contain a value of the type being explicitly cast to), e.g.
variant_value as Vector3?
and casting to non-nullable value types to be unsafe. But until nullable types are a thing, I would rather still be able to writevariant_value as [Type]
to explicitly mark intentional casts, rather than be required to use implicit casts to work around the error, or have to turn the UNSAFE_CAST error/warning off entirely and lose its benefits in other situations. #76843In my opinion, adding built-in functions such as
int_convert
,float_convert
,vector3_convert
, etc. for each possible type to use instead of casting withas
would also be an acceptable resolution, albeit not an ideal one, with these functions returning the corresponding types directly rather than returning a Variant as withtype_convert
.(Edit: Oh, when writing the above, I assumed that invalid casts like
0 as String
also produced an UNSAFE_CAST error/warning, but it seems that this case produces a different "Invalid cast" error and UNSAFE_CAST doesn't actually apply to anything except Variants. I guess it's unclear what benefit this error/warning has in any case, then, if it forbids implicit casts but not explicit casts, and if it's unsafe now even to cast a Variant to a reference (nullable) type?)Steps to reproduce
Minimum reproduction:
Turn on UNSAFE_CAST warnings/errors in project settings:
An UNSAFE_CAST warning or error will occur for line 3 of this GDScript code:
Minimal reproduction project (MRP)
See trivial reproduction steps
The text was updated successfully, but these errors were encountered: