-
Notifications
You must be signed in to change notification settings - Fork 379
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
Gno type check #1424
Comments
I think there was typechecking in isEql but it got removed over time. Good catch. I'm a bit amused as to how none of the tests caught this thus far. Thank you for catching it. Maybe there ought to be two operators. One that does what isEql does now, and another that does type checking, and the preprocessor can figure out whether runtime type checking is needed or not. There are a range of solutions, and let's discuss the pros and cons of them before implementing, unless you're happy to delete work and try another route. (I am, so if you are no problem. Or if you're feeling lucky...).
Here's the type checking model we should be gunning for.
There's nothing wrong with the VM itself doing useful work with invalid Go code. The solution should be in the precompiler.
We do, but it seems we missed a case for switch case assignments. |
I agree with this 100%. If I understand correctly, we plan to leverage Go's type-check tools as the first assurance before relying on GnoVM type check, and keep on perfecting the gno type check features, right? In addition to this, I propose executing precompiled files in Go during the test, and catch the result as a comparison with the GnoVM, like:
Introducing this approach will make it more convenient for individuals to identify differences, ensuring that Gno not only matches Go in terms of type-checking but also in the execution behavior. |
**Pinned Update:** The original #1426 is now divided into 4 parts, with the dependency relationship being: #1426 < #1775, #1426 <- https://github.com/gnolang/gno/pull/1890<- #1891. Among these, the main part, #1426, has been supplemented and optimized for the missing parts in the type checks of the original implementation, specifically as follows: - A new layer for type check is added(type_check.go). during the preprocess stage, the compatibility of operators and operands in expressions is checked, such as 1 - "a". This part used to be implemented as a runtime error, but now it is checked in type_check.go; - Modifications have been made to checkOrConvertType to add conversion checks for constants, such as int(1) + int64(1), which previously would not trigger a compile-time error; - Refined and improved several aspects of the handling logic for BinaryExpr during the preprocessing stage. - The existing checkType has been renamed to assertAssignableTo. ==========================update complete======================= ### Problem Definition Please proceed to #1424. ======update: fix #1462 , tests located in `gnovm/tests/files/type2`. this issue is fixed since they share the same contexts of type check and conversion. briefly for #1462, type of shift expression (or any composed expression involved shift expression) will be determined in the context they are used if they are untyped, also can be mutated by explicitly conversion with a `type call`. ========================================================================================== ### Overview of Solution #### checkOperandWithOp function: **Purpose**: Newly introduced to evaluate operand compatibility before deep type analysis. **Functionality**: Employs predefined rules to quickly identify incompatible patterns (e.g., "a" << 1 is flagged as incompatible). **Advantage**: Prevents unnecessary processing by checkOrConvertType for clear mismatches. #### checkOrConvertType function: **Role**: Engages after checkOperandWithOp's clearance. It's the hub for core type checking and conversion. **Key Improvement**: Enhanced handling of const conversions by limiting it within a certain range. **Example**: In cases like int(1) + int(8), the issue of unregulated const conversion is addressed. **Constraints**: Mandatory const conversion is now limited to specific scenarios (e.g., explicit conversion, operand in array/slice index, RHS of a shift expression). ### Specific Problems Solved 1. **assignable and sameType check:** This code should output "something else". the root cause for this is Error(0) is assignable to errCmp since it satisfies the interface of error, and result in inequality since the have different concrete type in runtime. Thanks @jaekwon for pointing out my mistake and give an improved version of this. ```go package main import ( "errors" "strconv" ) type Error int64 func (e Error) Error() string { return "error: " + strconv.Itoa(int(e)) } var errCmp = errors.New("XXXX") func main() { if Error(0) == errCmp { println("what the firetruck?") } else { println("something else") } } ``` 2. **Early Incompatibility Detection:** Conducted during preprocessing, not runtime. **Example**: ```go package main func main() { println(1 / "a") // Detects incompatibility early. } ``` ```go func main() { println(int(1) == int8(1)) // this is checked before checkOrConvertType if LHS and RHS are both typed. } ``` ~~3. **Implicit Conversion:**~~(this is split out) ~~Focus: Ensuring accurate conversions, particularly unnamed to named types.~~ ~~Example:~~ ~~```go~~ ~~package main~~ ~~type word uint~~ ~~type nat []word~~ ~~func (n nat) add() bool {~~ ~~ return true~~ ~~} ~~func Gen() nat {~~ ~~n := []word{0}~~ ~~return n~~ ~~}~~ ~~func main() {~~ ~~r := Gen()~~ ~~switch r.(type) {~~ ~~ case nat:~~ ~~println("nat")~~ ~~println(r.add())~~ ~~default:~~ ~~println("should not happen")~~ ~~ }~~ ~~}~~ ~~```~~ ~~4. **Type of Shift Expressions:**~~ ~~**Context**: Determines the type based on usage context and explicit conversions.~~ ~~**Implementation**: Additional checks in assignStmt, callExpr for potential untyped shift expressions (or else expressions with untyped shift expression embedded)~~~~appear, e.g. uint64(1 << x). This will trigger a potentially recursive check&convert until the shift expr got its final type.~~ ### Conclusion: This PR enhances the type check workflow and addresses previously overlooked aspects, resolving a variety of type-related issues. --------- Co-authored-by: Morgan <[email protected]> Co-authored-by: jaekwon <[email protected]>
This could enhance our lives, but I don't think it's essential for the launch. Does anyone disagree? |
Description:
The issue is from #1376 , and here gives an analysis and solution.
Q: Does this code successfully compile? If so, what is the expected output?
A: The code is expected to compile without issues, and logically, the output should be
something else
. However, when run in the gnovm environment, it produces the resultwhat the firetruck?.
The code successfully compiles because Error(0) is assignable to the type of errCmp. This is due to the fact that the type Error fulfills the same interface requirements as errCmp, making them compatible in terms of type assignment.
according to spec: "In any comparison, the first operand must be assignable to the type of the second operand, or vice versa."
Moreover, at runtime, they ought to be evaluated as unequal, given that they have distinct underlying types . The reason for the incorrect output lies in a type-checking issue: both Error(0) and errCmp are erroneously treated as if they are of the same int64 type, which is the underlying type of Error(0). Consequently, when evaluated as int64, their values are both 0, leading the comparison to incorrectly yield true.
Deeper analysis:
Upon further investigation, it was discovered that there are more issues with type checking.
In this instance, the code is expected to fail compilation because of a type mismatch. Surprisingly, it compiles and runs without error. This anomaly can be attributed to the absence of standard type checking, resulting in a forced and inappropriate type casting for all consts.
This code operates correctly, identifying an error with the illegal operand for the / operator. However, in the current setup, checks related to operators are performed at runtime. Ideally, these checks could and should be conducted during the preprocessing stage for enhanced efficiency and error handling.
This approach can be extended to multiple scenarios involving a variety of operators such as +, -, *, /, %, &, and others, encompassing a broader range of use cases and operator interactions.
Conclusion:
In this scenario, nat is a named type whose underlying type is []word. However, within the main function, the variable r should be recognized as type nat, but it is not being correctly asserted as such.
In conclusion, during the preprocessing stage, there is a noticeable absence of crucial checks. These include the verification of type mixtures, validation of operand compatibility, and the implicit conversion of types from unnamed to named.
Solution:
The solution is straight forward based on the analysis above.
1.regular type check for const.
This ensures standard scenarios, such as converting an untyped constant to a typed constant, function as intended (e.g., int(1) + 0). At the same time, it prohibits illegal conversions, like int(1) + int(8). It's important to note that in certain specific cases, this check may be bypassed. For example, an int8 can be used as a slice index since it will be forcefully converted to an int type.
e.g.
2. check operand compatibility with operators.
For binary expressions, unary expressions, and increment/decrement statements, it is essential to check the compatibility of operands during the preprocessing stage. This approach will effectively filter out most issues related to type compatibility. For example, it would identify and flag incompatible operations like 1 / "a".
3. checkOperand for special case
with / and %, divisor should not be zero.
4. check implicit unamed -> named, and convert
Expriment:
Here is a (WIP) PR that addresses all the aforementioned issues. While it is not yet complete, any feedback or suggestions for improvement would be greatly appreciated. #1426
======update:
also with this issue #1462
The text was updated successfully, but these errors were encountered: