From 6214fe925aa1f955f575f9523ab44f8e274d9c5a Mon Sep 17 00:00:00 2001 From: deelawn Date: Mon, 19 Aug 2024 23:40:05 +0100 Subject: [PATCH] fix(gnovm): handle non call expression valuedecl values (#2647) Closes #2636. With a value declaration like `var a, b = ...`, we currently only support a call expression on the RHS. This PR supports type assertion and index expressions as well.
Contributors' checklist... - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [x] Provided any useful hints for running manual tests - [x] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- gnovm/pkg/gnolang/preprocess.go | 41 ++++++++++++++++++++++++++++----- gnovm/tests/files/var20.gno | 2 +- gnovm/tests/files/vardecl.gno | 23 ++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 gnovm/tests/files/vardecl.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 9cd345dbd78..ba60ead28f6 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2170,13 +2170,42 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { numNames := len(n.NameExprs) sts := make([]Type, numNames) // static types tvs := make([]TypedValue, numNames) + if numNames > 1 && len(n.Values) == 1 { - // special case if `var a, b, c T? = f()` form. - cx := n.Values[0].(*CallExpr) - tt := evalStaticTypeOfRaw(store, last, cx).(*tupleType) - if rLen := len(tt.Elts); rLen != numNames { - panic(fmt.Sprintf("assignment mismatch: %d variable(s) but %s returns %d value(s)", numNames, cx.Func.String(), rLen)) + // Special cases if one of the following: + // - `var a, b, c T = f()` + // - `var a, b = n.(T)` + // - `var a, b = n[i], where n is a map` + + var tuple *tupleType + valueExpr := n.Values[0] + valueType := evalStaticTypeOfRaw(store, last, valueExpr) + + switch expr := valueExpr.(type) { + case *CallExpr: + tuple = valueType.(*tupleType) + case *TypeAssertExpr, *IndexExpr: + tuple = &tupleType{Elts: []Type{valueType, BoolType}} + if ex, ok := expr.(*TypeAssertExpr); ok { + ex.HasOK = true + break + } + expr.(*IndexExpr).HasOK = true + default: + panic(fmt.Sprintf("unexpected ValueDecl value expression type %T", expr)) } + + if rLen := len(tuple.Elts); rLen != numNames { + panic( + fmt.Sprintf( + "assignment mismatch: %d variable(s) but %s returns %d value(s)", + numNames, + valueExpr.String(), + rLen, + ), + ) + } + if n.Type != nil { // only a single type can be specified. nt := evalStaticType(store, last, n.Type) @@ -2188,7 +2217,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } else { // set types as return types. for i := 0; i < numNames; i++ { - et := tt.Elts[i] + et := tuple.Elts[i] sts[i] = et tvs[i] = anyValue(et) } diff --git a/gnovm/tests/files/var20.gno b/gnovm/tests/files/var20.gno index e2455cbaed8..6e15fcca6c5 100644 --- a/gnovm/tests/files/var20.gno +++ b/gnovm/tests/files/var20.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/var20.gno:8:6: assignment mismatch: 3 variable(s) but r returns 1 value(s) +// main/files/var20.gno:8:6: assignment mismatch: 3 variable(s) but r() returns 1 value(s) diff --git a/gnovm/tests/files/vardecl.gno b/gnovm/tests/files/vardecl.gno new file mode 100644 index 00000000000..34390f26a6a --- /dev/null +++ b/gnovm/tests/files/vardecl.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var i interface{} = 1 + var a, ok = i.(int) + println(a, ok) + + var b, c = doSomething() + println(b, c) + + m := map[string]int{"a": 1, "b": 2} + var d, okk = m["d"] + println(d, okk) +} + +func doSomething() (int, string) { + return 4, "hi" +} + +// Output: +// 1 true +// 4 hi +// 0 false