From 1180def181d7c74dc8229e4d2534054ed2d41d9b Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:54:59 -0700 Subject: [PATCH] feat: named and unnamed type assignment 3 of 3 (#2367) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the last part of the solution for issue #1141. The 1 of 3 of the solution can be found in PR #1143. The 2 of 3 of the solution can be found in PR #1246 It decomposes function calls that return multiple values in the preprocess. ### Here is the problem to solve: ` u1, n2 = x() ` How do we ensure that the returned multiple values from a function call adhere to named and unnamed type assignment specifications? Additionally, we want to solve this problem during preprocessing instead of at runtime to minimize the impact on runtime performance. ### The main ideas: u1, n2 = x() << decompose the statement to the following two lines // .tmp_1, .tmp_2 := x() // u1, n2 = .tmp_1, .tmp_2 then we can apply name and unname type conversion specs to the second line. u1, n2 = _tmp_1, _tmp_2 ### Here are the example code and the explanation ``` // decompose_filetest.gno package main type nat []int func x() (nat, []int) { a := nat{1} b := []int{2} return a, b } func main() { var u1 []int var n2 nat u1, n2 = x() // .tmp_1, .tmp_2 := x() // u1, n2 = .tmp_1, .tmp_2 println(u1) println(n2) } // Output: // slice[(1 int)] // (slice[(2 int)] main.nat) ``` ### Here is the simplified recursive tree of the transformation in the preprocess image ### Here are the major steps involved in this decomposition during preprocessing: - Create hidden temporary name expressions .tmp1, .tmp2. In Go, a leading dot is not valid in variable names, ensuring that users cannot create names that clash with these hidden variables. - Create two statements in the block: one for defining and one for assigning. ``` .tmp1, .tmp2 := x() u1, n2 = .tmp_1, .tmp_2 ``` - Preprocess each newly created statements - Replace the original statement with the two newly created statements. ### Here are two additional changes to facilitate above. - Update the FuncValue's body in `updates := pn.PrepareNewValues(pv) `since its source Body has been changed during preprocessing. - Replace all ` for index := range Body` with `for i:=0; i < len(Body); i++` in transcribe.go since the body length might change due to the decomposition.
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 - [ ] Updated the official documentation or not needed - [x] No breaking changes were made - [x] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] 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).
--------- Co-authored-by: Miloš Živković Co-authored-by: Morgan --- gnovm/Makefile | 2 +- gnovm/pkg/gnolang/nodes.go | 28 +++++ gnovm/pkg/gnolang/preprocess.go | 105 +++++++++++++++++- gnovm/pkg/gnolang/transcribe.go | 27 +++-- gnovm/pkg/gnolang/values.go | 9 ++ .../decompose_unnamed1a_filetest.gno | 26 +++++ .../decompose_unnamed1b_filetest.gno | 26 +++++ .../decompose/decompose_unnamed2_filetest.gno | 30 +++++ .../decompose_unnamed2b_filetest.gno | 48 ++++++++ .../decompose_unnamed2c_filetest.gno | 69 ++++++++++++ .../decompose_unnamed2d_filetest.gno | 71 ++++++++++++ .../decompose_unnamed2e_filetest.gno | 52 +++++++++ .../decompose_unnamed2f2_filetest.gno | 46 ++++++++ .../decompose_unnamed2f_filetest.gno | 49 ++++++++ .../decompose_unnamed2g_filetest.gno | 63 +++++++++++ .../decompose/decompose_unnamed3_filetest.gno | 50 +++++++++ .../decompose/decompose_unnamed4_filetest.gno | 40 +++++++ .../decompose_unnamed4b_filetest.gno | 19 ++++ .../decompose_unnamed4c_filetest.gno | 19 ++++ .../decompose/decompose_unnamed_filetest.gno | 26 +++++ 20 files changed, 793 insertions(+), 12 deletions(-) create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1a_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1b_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2b_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2c_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2d_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2e_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f2_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2g_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed3_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4b_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4c_filetest.gno create mode 100644 gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed_filetest.gno diff --git a/gnovm/Makefile b/gnovm/Makefile index 6e939289fb8..5ff3af9c253 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -53,7 +53,7 @@ lint: $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint run --config ../.github/golangci.yml ./... .PHONY: fmt -fmt: +fmt: go run ./cmd/gno fmt $(GNOFMT_FLAGS) ./stdlibs/... $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) . diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index a68e3ba9d0b..c578d2a3ec3 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -672,6 +672,10 @@ func (ss Body) GetBody() Body { return ss } +func (ss *Body) SetBody(nb Body) { + *ss = nb +} + func (ss Body) GetLabeledStmt(label Name) (stmt Stmt, idx int) { for idx, stmt = range ss { if label == stmt.GetLabel() { @@ -1375,6 +1379,13 @@ func (x *PackageNode) PrepareNewValues(pv *PackageValue) []TypedValue { panic("PackageNode.PrepareNewValues() package mismatch") } } + // The FuncValue Body may have been altered during the preprocessing. + // We need to update body field from the source in the FuncValue accordingly. + for _, tv := range x.Values { + if fv, ok := tv.V.(*FuncValue); ok { + fv.UpdateBodyFromSource() + } + } pvl := len(block.Values) pnl := len(x.Values) // copy new top-level defined values/types. @@ -1480,6 +1491,7 @@ type BlockNode interface { Define(Name, TypedValue) Define2(bool, Name, Type, TypedValue) GetBody() Body + SetBody(Body) } // ---------------------------------------- @@ -1873,18 +1885,34 @@ func (x *IfStmt) GetBody() Body { panic("IfStmt has no body (but .Then and .Else do)") } +func (x *IfStmt) SetBody(b Body) { + panic("IfStmt has no body (but .Then and .Else do)") +} + func (x *SwitchStmt) GetBody() Body { panic("SwitchStmt has no body (but its cases do)") } +func (x *SwitchStmt) SetBody(b Body) { + panic("SwitchStmt has no body (but its cases do)") +} + func (x *FileNode) GetBody() Body { panic("FileNode has no body (but it does have .Decls)") } +func (x *FileNode) SetBody(b Body) { + panic("FileNode has no body (but it does have .Decls)") +} + func (x *PackageNode) GetBody() Body { panic("PackageNode has no body") } +func (x *PackageNode) SetBody(b Body) { + panic("PackageNode has no body") +} + // ---------------------------------------- // Value Path diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 11c2708ff80..d21e9bf0efd 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "reflect" + "strings" "sync/atomic" "github.com/gnolang/gno/tm2/pkg/errors" @@ -426,6 +427,24 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { switch n := n.(type) { // TRANS_ENTER ----------------------- case *AssignStmt: + if n.Op == DEFINE { + for _, lx := range n.Lhs { + ln := lx.(*NameExpr).Name + if ln == blankIdentifier { + // ignore. + } else if strings.HasPrefix(string(ln), ".decompose_") { + _, ok := last.GetLocalIndex(ln) + if !ok { + // initial declaration to be re-defined. + last.Predefine(false, ln) + } else { + // do not redeclare. + } + } + } + } else { + // nothing defined. + } // TRANS_ENTER ----------------------- case *ImportDecl, *ValueDecl, *TypeDecl, *FuncDecl: @@ -1872,7 +1891,88 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } else { // ASSIGN, or assignment operation (+=, -=, <<=, etc.) // NOTE: Keep in sync with DEFINE above. if len(n.Lhs) > len(n.Rhs) { - // check is done in assertCompatible + // check is done in assertCompatible where we also + // asserted we have at lease one element in Rhs + if cx, ok := n.Rhs[0].(*CallExpr); ok { + // we decompose the a,b = x(...) for named and unamed + // type value return in an assignments + // Call case: a, b = x(...) + ift := evalStaticTypeOf(store, last, cx.Func) + cft := getGnoFuncTypeOf(store, ift) + // check if we we need to decompose for named typed conversion in the function return results + var decompose bool + + for i, rhsType := range cft.Results { + lt := evalStaticTypeOf(store, last, n.Lhs[i]) + if lt != nil && isNamedConversion(rhsType.Type, lt) { + decompose = true + break + } + } + if decompose { + // only enter this section if cft.Results to be converted to Lhs type for named type conversion. + // decompose a,b = x() + // .decompose1, .decompose2 := x() assignment statement expression (Op=DEFINE) + // a,b = .decompose1, .decompose2 assignment statement expression ( Op=ASSIGN ) + // add the new statement to last.Body + + // step1: + // create a hidden var with leading . (dot) the curBodyLen increase every time when there is a decomposition + // because there could be multiple decomposition happens + // we use both stmt index and return result number to differentiate the .decompose variables created in each assignment decompostion + // ex. .decompose_3_2: this variable is created as the 3rd statement in the block, the 2nd parameter returned from x(), + // create .decompose_1_1, .decompose_1_2 .... based on number of result from x() + tmpExprs := make(Exprs, 0, len(cft.Results)) + for i := range cft.Results { + rn := fmt.Sprintf(".decompose_%d_%d", index, i) + tmpExprs = append(tmpExprs, Nx(rn)) + } + // step2: + // .decompose1, .decompose2 := x() + dsx := &AssignStmt{ + Lhs: tmpExprs, + Op: DEFINE, + Rhs: n.Rhs, + } + dsx.SetLine(n.Line) + dsx = Preprocess(store, last, dsx).(*AssignStmt) + + // step3: + + // a,b = .decompose1, .decompose2 + // assign stmt expression + // The right-hand side will be converted to a call expression for named/unnamed conversion. + // tmpExprs is a []Expr; we make a copy of tmpExprs to prevent dsx.Lhs in the previous statement (dsx) from being changed by side effects. + // If we don't copy tmpExprs, when asx.Rhs is converted to a const call expression during the preprocessing of the AssignStmt asx, + // dsx.Lhs will change from []NameExpr to []CallExpr. + // This side effect would cause a panic when the machine executes the dsx statement, as it expects Lhs to be []NameExpr. + + asx := &AssignStmt{ + Lhs: n.Lhs, + Op: ASSIGN, + Rhs: copyExprs(tmpExprs), + } + asx.SetLine(n.Line) + asx = Preprocess(store, last, asx).(*AssignStmt) + + // step4: + // replace the original stmt with two new stmts + body := last.GetBody() + // we need to do an in-place replacement while leaving the current node + n.Attributes = dsx.Attributes + n.Lhs = dsx.Lhs + n.Op = dsx.Op + n.Rhs = dsx.Rhs + + // insert a assignment statement a,b = .decompose1,.decompose2 AFTER the current statement in the last.Body. + body = append(body[:index+1], append(Body{asx}, body[index+1:]...)...) + last.SetBody(body) + } // end of the decomposition + + // Last step: we need to insert the statements to FuncValue.body of PackageNopde.Values[i].V + // updating FuncValue.body=FuncValue.Source.Body in updates := pn.PrepareNewValues(pv) during preprocess. + // we updated FuncValue from source. + } } else { // len(Lhs) == len(Rhs) if n.Op == SHL_ASSIGN || n.Op == SHR_ASSIGN { if len(n.Lhs) != 1 || len(n.Rhs) != 1 { @@ -3160,7 +3260,7 @@ func predefineNow2(store Store, last BlockNode, d Decl, m map[Name]struct{}) (De } else { panic("should not happen") } - + // The body may get altered during preprocessing later. if !dt.TryDefineMethod(&FuncValue{ Type: ft, IsMethod: true, @@ -3388,6 +3488,7 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) { pkg := skipFile(last).(*PackageNode) // define a FuncValue w/ above type as d.Name. // fill in later during *FuncDecl:BLOCK. + // The body may get altered during preprocessing later. fv := &FuncValue{ Type: ft, IsMethod: false, diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index c5b72336c83..28e97ff2b5b 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -271,7 +271,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncLitExpr) } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNCLIT_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break @@ -383,7 +384,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*BlockStmt) } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_BLOCK_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break @@ -393,7 +395,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } case *BranchStmt: case *DeclStmt: - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_DECL_BODY, idx, cnn.Body[idx], &c).(SimpleDeclStmt) if isBreak(c) { break @@ -438,7 +441,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_FOR_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break @@ -488,7 +492,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*IfCaseStmt) } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_IF_CASE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break @@ -525,7 +530,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_RANGE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break @@ -565,7 +571,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_SELECTCASE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break @@ -640,7 +647,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_SWITCHCASE_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break @@ -666,7 +674,8 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc } else { cnn = cnn2.(*FuncDecl) } - for idx := range cnn.Body { + // iterate over Body; its length can change if a statement is decomposed. + for idx := 0; idx < len(cnn.Body); idx++ { cnn.Body[idx] = transcribe(t, nns, TRANS_FUNC_BODY, idx, cnn.Body[idx], &c).(Stmt) if isBreak(c) { break diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 9b44a3c71e7..5da7c15bb05 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -601,6 +601,15 @@ func (fv *FuncValue) GetBodyFromSource(store Store) []Stmt { return fv.body } +func (fv *FuncValue) UpdateBodyFromSource() { + if fv.Source == nil { + panic(fmt.Sprintf( + "Source is missing for FuncValue %q", + fv.Name)) + } + fv.body = fv.Source.GetBody() +} + func (fv *FuncValue) GetSource(store Store) BlockNode { if rn, ok := fv.Source.(RefNode); ok { source := store.GetBlockNode(rn.GetLocation()) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1a_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1a_filetest.gno new file mode 100644 index 00000000000..0e999ca130f --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1a_filetest.gno @@ -0,0 +1,26 @@ +package main + +type nat []int + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} + +func main() { + var u1 []int + var n2 nat + + _, n2 = x() + // .tmp1, .tmp_2 := x() + // _, u2 = .tmp1, .tmp_2 + + println(u1) + println(n2) + +} + +// Output: +// (nil []int) +// (slice[(2 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1b_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1b_filetest.gno new file mode 100644 index 00000000000..9fed8f29cc6 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed1b_filetest.gno @@ -0,0 +1,26 @@ +package main + +type nat []int + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} + +func main() { + var u1 []int + var n2 nat + + u1, _ = x() + // .tmp1, .tmp_2 := x() + // u1, _ = .tmp1, .tmp_2 + + println(u1) + println(n2) + +} + +// Output: +// slice[(1 int)] +// (nil main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2_filetest.gno new file mode 100644 index 00000000000..db8a0838c7a --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2_filetest.gno @@ -0,0 +1,30 @@ +package main + +type nat []int + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} + +func main() { + var u1 []int + var n2 nat + // BlockStmt + { + u1, n2 = x() + // .tmp0_1, .tmp0_2 := x() + // u1, n2 = .tmp0_1, .tmp0_2 + println(u1) + println(n2) + println(u1) + println(n2) + } +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2b_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2b_filetest.gno new file mode 100644 index 00000000000..6d8f05807c7 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2b_filetest.gno @@ -0,0 +1,48 @@ +package main + +type nat []int + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + var u1 []int + var n2 nat + // if block + if true { + u1, n2 = x() + // .tmp_1, .tmp_2 := x() + // u1, n2 = .tmp_1, .tmp_2 + println(u1) + println(n2) + println(u1) + println(n2) + } + // else block + if false { + + } else { + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + } +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2c_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2c_filetest.gno new file mode 100644 index 00000000000..cae371f3821 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2c_filetest.gno @@ -0,0 +1,69 @@ +package main + +type nat []int + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + var u1 []int + var n2 nat + + // if, for, range block + + if true { + u1, n2 = x() + println(u1) + println(n2) + u1, n2 = y() + println(u1) + println(n2) + + for i := 0; i < 2; i++ { + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + } + + zeros := []int{0, 0} + for _, _ = range zeros { + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + } + + } +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2d_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2d_filetest.gno new file mode 100644 index 00000000000..4905b3d58de --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2d_filetest.gno @@ -0,0 +1,71 @@ +package main + +type nat []int + +// gloabal variables +var u1 []int +var n2 nat + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + + // else-if, for block + if false { + + } else if true { + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + + for i := 0; i < 2; i++ { + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + } + } +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2e_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2e_filetest.gno new file mode 100644 index 00000000000..d73e908f6c6 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2e_filetest.gno @@ -0,0 +1,52 @@ +package main + +type nat []int + +// gloabal variables +var u1 []int +var n2 nat + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + // function block + fn() +} + +func fn() { + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + + for i := 0; i < 2; i++ { + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + } +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f2_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f2_filetest.gno new file mode 100644 index 00000000000..ea8e042d18b --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f2_filetest.gno @@ -0,0 +1,46 @@ +package main + +type nat []int + +// gloabal variables +var u1 []int +var n2 nat + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} + +func main() { + fn() +} + +func fn() { + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + + // function literal block + u1, n2 = func() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b + }() + println(u1) + println(n2) + println(u1) + println(n2) +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f_filetest.gno new file mode 100644 index 00000000000..05201a6bd2b --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2f_filetest.gno @@ -0,0 +1,49 @@ +package main + +type nat []int + +// gloabal variables +var u1 []int +var n2 nat + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + fn() +} + +func fn() { + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + + // function literal block + func() { + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + + }() +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2g_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2g_filetest.gno new file mode 100644 index 00000000000..c8f9badc380 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed2g_filetest.gno @@ -0,0 +1,63 @@ +package main + +type nat []int + +// gloabal variables +var u1 []int +var n2 nat + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + fn() +} + +func fn() { + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + // for block + // switch case block + + for i := 0; i < 2; i++ { + switch i { + case 0: + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + default: + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + + } + } +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed3_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed3_filetest.gno new file mode 100644 index 00000000000..c8749cd862a --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed3_filetest.gno @@ -0,0 +1,50 @@ +package main + +type nat []int + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + var u1 []int + var n2 nat + + // multiple statements + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + u1, n2 = y() + println(u1) + println(n2) + println(u1) + println(n2) + u1, n2 = x() + println(u1) + println(n2) + println(u1) + println(n2) + +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(4 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) +// slice[(1 int)] +// (slice[(2 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4_filetest.gno new file mode 100644 index 00000000000..06617a90ba6 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4_filetest.gno @@ -0,0 +1,40 @@ +package main + +type nat []int + +// package block +var n1, u2 = x() + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} +func y() (nat, []int) { + a := nat{3} + b := []int{4} + return a, b +} +func main() { + + // multiple statements + println(n1) + println(u2) + + u2, n1 = y() + println(n1) + println(u2) + + n1, u2 = x() + println(n1) + println(u2) + +} + +// Output: +// (slice[(1 int)] main.nat) +// slice[(2 int)] +// (slice[(4 int)] main.nat) +// slice[(3 int)] +// (slice[(1 int)] main.nat) +// slice[(2 int)] diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4b_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4b_filetest.gno new file mode 100644 index 00000000000..e84fdf99567 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4b_filetest.gno @@ -0,0 +1,19 @@ +package main + +type nat []int + +// package block +var n1, n2 nat = x() + +func x() (nat, []int) { + return nat{1}, nat{2} +} + +func main() { + println(n1) + println(n2) +} + +// Output: +// (slice[(1 int)] main.nat) +// (slice[(2 int)] main.nat) diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4c_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4c_filetest.gno new file mode 100644 index 00000000000..863649f3705 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed4c_filetest.gno @@ -0,0 +1,19 @@ +package main + +type nat []int + +// package block +var u1, u2 []int = x() + +func x() (nat, []int) { + return []int{1}, nat{2} +} + +func main() { + println(u1) + println(u2) +} + +// Output: +// slice[(1 int)] +// slice[(2 int)] diff --git a/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed_filetest.gno b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed_filetest.gno new file mode 100644 index 00000000000..44f9992a269 --- /dev/null +++ b/gnovm/tests/files/assign_unnamed_type/decompose/decompose_unnamed_filetest.gno @@ -0,0 +1,26 @@ +package main + +type nat []int + +func x() (nat, []int) { + a := nat{1} + b := []int{2} + return a, b +} + +func main() { + var u1 []int + var n2 nat + + u1, n2 = x() + // .tmp_1, .tmp_2 := x() + // u1, n2 = .tmp_1, .tmp_2 + + println(u1) + println(n2) + +} + +// Output: +// slice[(1 int)] +// (slice[(2 int)] main.nat)