From 57dc54ca6a1172668024f00a857563b813592384 Mon Sep 17 00:00:00 2001 From: hthieu1110 Date: Mon, 25 Nov 2024 01:45:13 -0800 Subject: [PATCH] feat(gnovm): returns error like in Golang when assignment with a function which does not return any value (#3049) This PR aims at resolving this issue: https://github.com/gnolang/gno/issues/1082 This depends on https://github.com/gnolang/gno/pull/3017 because it refactored the code to sync the logic/code between AssignStmt vs ValueDecl. Related https://github.com/gnolang/gno/pull/2695
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests
--------- Co-authored-by: hieu.ha Co-authored-by: Mikael VALLENET --- gnovm/pkg/gnolang/preprocess.go | 20 +++++++------------- gnovm/pkg/gnolang/type_check.go | 8 ++++++++ gnovm/tests/files/assign37.gno | 10 ++++++++++ gnovm/tests/files/assign37b.gno | 12 ++++++++++++ gnovm/tests/files/var34.gno | 10 ++++++++++ gnovm/tests/files/var34b.gno | 11 +++++++++++ gnovm/tests/files/var34c.gno | 10 ++++++++++ 7 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 gnovm/tests/files/assign37.gno create mode 100644 gnovm/tests/files/assign37b.gno create mode 100644 gnovm/tests/files/var34.gno create mode 100644 gnovm/tests/files/var34b.gno create mode 100644 gnovm/tests/files/var34c.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 85a846535ce..b9eb756512e 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2373,7 +2373,7 @@ func defineOrDecl( sts := make([]Type, numNames) // static types tvs := make([]TypedValue, numNames) - if numNames > 1 && len(valueExprs) == 1 { + if numVals == 1 && numNames > 1 { parseMultipleAssignFromOneExpr(sts, tvs, store, bn, nameExprs, typeExpr, valueExprs[0]) } else { parseAssignFromExprList(sts, tvs, store, bn, isConst, nameExprs, typeExpr, valueExprs) @@ -2410,7 +2410,7 @@ func parseAssignFromExprList( for _, v := range valueExprs { if cx, ok := v.(*CallExpr); ok { tt, ok := evalStaticTypeOfRaw(store, bn, cx).(*tupleType) - if ok && len(tt.Elts) != 1 { + if ok && len(tt.Elts) > 1 { panic(fmt.Sprintf("multiple-value %s (value of type %s) in single-value context", cx.Func.String(), tt.Elts)) } } @@ -3132,18 +3132,12 @@ func gnoTypeOf(store Store, t Type) Type { // but rather computes the type OF x. func evalStaticTypeOf(store Store, last BlockNode, x Expr) Type { t := evalStaticTypeOfRaw(store, last, x) - if tt, ok := t.(*tupleType); ok { - if len(tt.Elts) != 1 { - panic(fmt.Sprintf( - "evalStaticTypeOf() only supports *CallExpr with 1 result, got %s", - tt.String(), - )) - } else { - return tt.Elts[0] - } - } else { - return t + + if tt, ok := t.(*tupleType); ok && len(tt.Elts) == 1 { + return tt.Elts[0] } + + return t } // like evalStaticTypeOf() but returns the raw *tupleType for *CallExpr. diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index c62d67375ee..e786bed683f 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -1029,6 +1029,14 @@ func assertValidAssignRhs(store Store, last BlockNode, n Node) { tt = evalStaticType(store, last, exp) panic(fmt.Sprintf("%s (type) is not an expression", tt.String())) } + + // Ensures that function used in ValueDecl or AssignStmt must return at least 1 value. + if cx, ok := exp.(*CallExpr); ok { + tType, ok := tt.(*tupleType) + if ok && len(tType.Elts) == 0 { + panic(fmt.Sprintf("%s (no value) used as value", cx.Func.String())) + } + } } } diff --git a/gnovm/tests/files/assign37.gno b/gnovm/tests/files/assign37.gno new file mode 100644 index 00000000000..d96aab42070 --- /dev/null +++ b/gnovm/tests/files/assign37.gno @@ -0,0 +1,10 @@ +package main + +func f() { } + +func main() { + a := f() +} + +// Error: +// main/files/assign37.gno:6:2: f (no value) used as value diff --git a/gnovm/tests/files/assign37b.gno b/gnovm/tests/files/assign37b.gno new file mode 100644 index 00000000000..42e3dbe6e1d --- /dev/null +++ b/gnovm/tests/files/assign37b.gno @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func f() { } + +func main() { + a, b := f(), f() +} + +// Error: +// main/files/assign37b.gno:8:2: f (no value) used as value diff --git a/gnovm/tests/files/var34.gno b/gnovm/tests/files/var34.gno new file mode 100644 index 00000000000..4f1dff6183f --- /dev/null +++ b/gnovm/tests/files/var34.gno @@ -0,0 +1,10 @@ +package main + +func f() {} + +func main() { + var t = f() +} + +// Error: +// main/files/var34.gno:6:6: f (no value) used as value diff --git a/gnovm/tests/files/var34b.gno b/gnovm/tests/files/var34b.gno new file mode 100644 index 00000000000..21e8a89bb14 --- /dev/null +++ b/gnovm/tests/files/var34b.gno @@ -0,0 +1,11 @@ +package main + +func f() {} + +func main() { + var a int + a = f() +} + +// Error: +// main/files/var34b.gno:7:2: f (no value) used as value diff --git a/gnovm/tests/files/var34c.gno b/gnovm/tests/files/var34c.gno new file mode 100644 index 00000000000..18ab996eefd --- /dev/null +++ b/gnovm/tests/files/var34c.gno @@ -0,0 +1,10 @@ +package main + +func f() {} + +func main() { + var a, b int = f(), 1 +} + +// Error: +// main/files/var34c.gno:6:6: f (no value) used as value