From d9e7bc77e6284b1136a956d74dc1fe8d6d431fb9 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Wed, 13 Mar 2024 18:54:27 +0100 Subject: [PATCH 1/3] for loop closure fix for simple for loops --- a.gno | 12 ++++++++++++ gnovm/pkg/gnolang/machine.go | 10 ++++++++++ gnovm/pkg/gnolang/op_exec.go | 22 ++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 a.gno diff --git a/a.gno b/a.gno new file mode 100644 index 00000000000..499b2aedd93 --- /dev/null +++ b/a.gno @@ -0,0 +1,12 @@ +package main + +func main() { + var fns []func() + for _, v := range []int{1, 2, 3} { + x := v*100 + v + fns = append(fns, func() { println(x) }) + } + for _, fn := range fns { + fn() + } +} diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index e9e8eba8adc..55585d1d809 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -1513,6 +1513,16 @@ func (m *Machine) ForcePopStmt() (s Stmt) { return } +func (m *Machine) ForceSwapStmt(target Stmt) { + lastStmt := len(m.Stmts) - 1 + if debug { + s := m.Stmts[lastStmt] + m.Printf("-s %v\n", s) + m.Printf("+s %v\n", target) + } + m.Stmts[lastStmt] = target +} + // Offset starts at 1. func (m *Machine) PeekExpr(offset int) Expr { return m.Exprs[len(m.Exprs)-offset] diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 300303135ad..f6dee02c6c5 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -114,6 +114,28 @@ func (m *Machine) doOpExec(op Op) { s = next goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { + // Create new block. This allows any closures generated within to + // reference the correct block, without having values changed. + cur := m.PopBlock() + newBlock := m.Alloc.NewBlock(cur.Source, m.LastBlock()) + newBlock.bodyStmt = *bs + bs = &newBlock.bodyStmt + m.PushBlock(newBlock) + m.ForceSwapStmt(bs) + sb := cur.Source.(*ForStmt) + if as, ok := sb.Init.(*AssignStmt); ok && as.Op == DEFINE { + // There is an init statement and it defines values. + // Copy over the values to the new block. + for i := 0; i < len(as.Lhs); i++ { + // Get name and value of i'th term. + nx := as.Lhs[i].(*NameExpr) + // Finally, define (or assign if loop block). + curPtr := cur.GetPointerTo(m.Store, nx.Path) + ptr := newBlock.GetPointerTo(m.Store, nx.Path) + ptr.Assign2(m.Alloc, m.Store, m.Realm, *curPtr.TV, true) + } + } + // (queue to) go back. if bs.Cond != nil { m.PushExpr(bs.Cond) From 278add632c6957c2851261c6040175c3f2efd937 Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Wed, 13 Mar 2024 19:21:47 +0100 Subject: [PATCH 2/3] copy soln to other occurences --- gnovm/pkg/gnolang/op_exec.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index f6dee02c6c5..3ffa695a22a 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -122,6 +122,7 @@ func (m *Machine) doOpExec(op Op) { bs = &newBlock.bodyStmt m.PushBlock(newBlock) m.ForceSwapStmt(bs) + // TODO: instead of relying on source, maybe use .Op of bodystmt sb := cur.Source.(*ForStmt) if as, ok := sb.Init.(*AssignStmt); ok && as.Op == DEFINE { // There is an init statement and it defines values. @@ -129,7 +130,7 @@ func (m *Machine) doOpExec(op Op) { for i := 0; i < len(as.Lhs); i++ { // Get name and value of i'th term. nx := as.Lhs[i].(*NameExpr) - // Finally, define (or assign if loop block). + // Define new name using value of old. curPtr := cur.GetPointerTo(m.Store, nx.Path) ptr := newBlock.GetPointerTo(m.Store, nx.Path) ptr.Assign2(m.Alloc, m.Store, m.Realm, *curPtr.TV, true) @@ -229,6 +230,15 @@ func (m *Machine) doOpExec(op Op) { goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { if bs.ListIndex < bs.ListLen-1 { + // Create new block. This allows any closures generated within to + // reference the correct block, without having values changed. + cur := m.PopBlock() + newBlock := m.Alloc.NewBlock(cur.Source, m.LastBlock()) + newBlock.bodyStmt = *bs + bs = &newBlock.bodyStmt + m.PushBlock(newBlock) + m.ForceSwapStmt(bs) + // set up next assign if needed. switch bs.Op { case ASSIGN: @@ -323,6 +333,15 @@ func (m *Machine) doOpExec(op Op) { goto EXEC_SWITCH } else if bs.NextBodyIndex == bs.BodyLen { if bs.StrIndex < bs.StrLen { + // Create new block. This allows any closures generated within to + // reference the correct block, without having values changed. + cur := m.PopBlock() + newBlock := m.Alloc.NewBlock(cur.Source, m.LastBlock()) + newBlock.bodyStmt = *bs + bs = &newBlock.bodyStmt + m.PushBlock(newBlock) + m.ForceSwapStmt(bs) + // set up next assign if needed. switch bs.Op { case ASSIGN: @@ -421,6 +440,15 @@ func (m *Machine) doOpExec(op Op) { m.PopFrameAndReset() return } else { + // Create new block. This allows any closures generated within to + // reference the correct block, without having values changed. + cur := m.PopBlock() + newBlock := m.Alloc.NewBlock(cur.Source, m.LastBlock()) + newBlock.bodyStmt = *bs + bs = &newBlock.bodyStmt + m.PushBlock(newBlock) + m.ForceSwapStmt(bs) + // set up next assign if needed. switch bs.Op { case ASSIGN: From a69768ee4601a244f2698111ba14c3d44ac277ee Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Wed, 13 Mar 2024 19:34:31 +0100 Subject: [PATCH 3/3] add maxwell tests --- gnovm/tests/debug/3_break.gno | 26 ++ gnovm/tests/debug/3_continue.gno | 26 ++ gnovm/tests/debug/3_goto.gno | 27 ++ gnovm/tests/debug2/1.gno | 26 ++ gnovm/tests/debug2/1a.gnoa | 23 ++ gnovm/tests/debug2/1a_goto.gno | 25 ++ gnovm/tests/debug2/1b.gno | 34 ++ gnovm/tests/debug2/1b_goto.gno | 36 ++ gnovm/tests/debug2/2.gno | 34 ++ gnovm/tests/debug2/3.gno | 25 ++ gnovm/tests/debug2/4.gno | 26 ++ gnovm/tests/debug2/5.gno | 33 ++ gnovm/tests/debug2/closure0.gno | 17 + gnovm/tests/debug2/closure11.gno | 25 ++ gnovm/tests/debug2/closure11_b.gno | 27 ++ gnovm/tests/debug2/closure12_a.gno | 23 ++ gnovm/tests/debug2/closure15_a.gno | 50 +++ gnovm/tests/debug2/closure16.gno | 21 + .../closure17_sort_search_efficiency.gno | 21 + gnovm/tests/debug2/closure9.gno | 26 ++ gnovm/tests/debug2/closure9_g.gno | 24 ++ gnovm/tests/debug2/closure9_h.gno | 30 ++ gnovm/tests/debug2/closure9_i.gno | 31 ++ gnovm/tests/debug2/declare.gno | 8 + gnovm/tests/debug2/defer.gno | 41 ++ gnovm/tests/debug2/foo1155.gno | 25 ++ gnovm/tests/debug2/inc.gno | 10 + gnovm/tests/debug2/return.gno | 9 + gnovm/tests/debug2/shadow.gno | 16 + gnovm/tests/debug2/sort_search_exhausted.gno | 20 + gnovm/tests/debug2/zrealm_avl1.gno | 379 ++++++++++++++++++ gnovm/tests/file_test.go | 7 + gnovm/tests/files/append5.gno | 2 +- gnovm/tests/files/closure10.gno | 26 ++ gnovm/tests/files/closure10_a.gno | 26 ++ gnovm/tests/files/closure11.gno | 25 ++ gnovm/tests/files/closure11_a.gno | 23 ++ gnovm/tests/files/closure11_b.gno | 27 ++ gnovm/tests/files/closure12.gno | 22 + gnovm/tests/files/closure12_a.gno | 23 ++ gnovm/tests/files/closure12_b.gno | 24 ++ gnovm/tests/files/closure12_c.gno | 27 ++ gnovm/tests/files/closure12_e.gno | 27 ++ gnovm/tests/files/closure12_f.gno | 20 + gnovm/tests/files/closure12_g.gno | 14 + gnovm/tests/files/closure12_h.gno | 25 ++ gnovm/tests/files/closure12_i.gno | 15 + gnovm/tests/files/closure13.gno | 22 + gnovm/tests/files/closure13_a.gno | 24 ++ gnovm/tests/files/closure13_b0.gno | 26 ++ gnovm/tests/files/closure13_b1.gno | 26 ++ gnovm/tests/files/closure13_b_stdlibs.gno | 47 +++ gnovm/tests/files/closure14.gno | 26 ++ gnovm/tests/files/closure14_a.gno | 27 ++ gnovm/tests/files/closure15.gno | 32 ++ gnovm/tests/files/closure15_a.gno | 49 +++ gnovm/tests/files/closure16.gno | 21 + gnovm/tests/files/closure16_a.gno | 20 + gnovm/tests/files/closure16_a1.gno | 19 + gnovm/tests/files/closure16_b.gno | 23 ++ gnovm/tests/files/closure16_b1.gno | 22 + gnovm/tests/files/closure17_io2.gno | 21 + gnovm/tests/files/closure17_recover4.gno | 25 ++ gnovm/tests/files/closure17_recover6.gno | 30 ++ gnovm/tests/files/closure17_recover6a.gno | 40 ++ .../closure17_sort_search_efficiency.gno | 21 + .../tests/files/closure17_zregexp_stdlibs.gno | 19 + gnovm/tests/files/closure9.gno | 30 ++ gnovm/tests/files/closure9_a0.gno | 29 ++ gnovm/tests/files/closure9_a02.gno | 20 + gnovm/tests/files/closure9_a1.gno | 34 ++ gnovm/tests/files/closure9_b.gno | 22 + gnovm/tests/files/closure9_c.gno | 18 + gnovm/tests/files/closure9_d.gno | 29 ++ gnovm/tests/files/closure9_e.gno | 12 + gnovm/tests/files/closure9_f.gno | 53 +++ gnovm/tests/files/closure9_g.gno | 24 ++ gnovm/tests/files/closure9_h.gno | 30 ++ gnovm/tests/files/closure9_h_0.gno | 28 ++ gnovm/tests/files/closure9_i.gno | 31 ++ gnovm/tests/files/closure9_j.gno | 21 + a.gno => gnovm/tests/files/closure_9a.gno | 5 + 82 files changed, 2402 insertions(+), 1 deletion(-) create mode 100644 gnovm/tests/debug/3_break.gno create mode 100644 gnovm/tests/debug/3_continue.gno create mode 100644 gnovm/tests/debug/3_goto.gno create mode 100644 gnovm/tests/debug2/1.gno create mode 100644 gnovm/tests/debug2/1a.gnoa create mode 100644 gnovm/tests/debug2/1a_goto.gno create mode 100644 gnovm/tests/debug2/1b.gno create mode 100644 gnovm/tests/debug2/1b_goto.gno create mode 100644 gnovm/tests/debug2/2.gno create mode 100644 gnovm/tests/debug2/3.gno create mode 100644 gnovm/tests/debug2/4.gno create mode 100644 gnovm/tests/debug2/5.gno create mode 100644 gnovm/tests/debug2/closure0.gno create mode 100644 gnovm/tests/debug2/closure11.gno create mode 100644 gnovm/tests/debug2/closure11_b.gno create mode 100644 gnovm/tests/debug2/closure12_a.gno create mode 100644 gnovm/tests/debug2/closure15_a.gno create mode 100644 gnovm/tests/debug2/closure16.gno create mode 100644 gnovm/tests/debug2/closure17_sort_search_efficiency.gno create mode 100644 gnovm/tests/debug2/closure9.gno create mode 100644 gnovm/tests/debug2/closure9_g.gno create mode 100644 gnovm/tests/debug2/closure9_h.gno create mode 100644 gnovm/tests/debug2/closure9_i.gno create mode 100644 gnovm/tests/debug2/declare.gno create mode 100644 gnovm/tests/debug2/defer.gno create mode 100644 gnovm/tests/debug2/foo1155.gno create mode 100644 gnovm/tests/debug2/inc.gno create mode 100644 gnovm/tests/debug2/return.gno create mode 100644 gnovm/tests/debug2/shadow.gno create mode 100644 gnovm/tests/debug2/sort_search_exhausted.gno create mode 100644 gnovm/tests/debug2/zrealm_avl1.gno create mode 100644 gnovm/tests/files/closure10.gno create mode 100644 gnovm/tests/files/closure10_a.gno create mode 100644 gnovm/tests/files/closure11.gno create mode 100644 gnovm/tests/files/closure11_a.gno create mode 100644 gnovm/tests/files/closure11_b.gno create mode 100644 gnovm/tests/files/closure12.gno create mode 100644 gnovm/tests/files/closure12_a.gno create mode 100644 gnovm/tests/files/closure12_b.gno create mode 100644 gnovm/tests/files/closure12_c.gno create mode 100644 gnovm/tests/files/closure12_e.gno create mode 100644 gnovm/tests/files/closure12_f.gno create mode 100644 gnovm/tests/files/closure12_g.gno create mode 100644 gnovm/tests/files/closure12_h.gno create mode 100644 gnovm/tests/files/closure12_i.gno create mode 100644 gnovm/tests/files/closure13.gno create mode 100644 gnovm/tests/files/closure13_a.gno create mode 100644 gnovm/tests/files/closure13_b0.gno create mode 100644 gnovm/tests/files/closure13_b1.gno create mode 100644 gnovm/tests/files/closure13_b_stdlibs.gno create mode 100644 gnovm/tests/files/closure14.gno create mode 100644 gnovm/tests/files/closure14_a.gno create mode 100644 gnovm/tests/files/closure15.gno create mode 100644 gnovm/tests/files/closure15_a.gno create mode 100644 gnovm/tests/files/closure16.gno create mode 100644 gnovm/tests/files/closure16_a.gno create mode 100644 gnovm/tests/files/closure16_a1.gno create mode 100644 gnovm/tests/files/closure16_b.gno create mode 100644 gnovm/tests/files/closure16_b1.gno create mode 100644 gnovm/tests/files/closure17_io2.gno create mode 100644 gnovm/tests/files/closure17_recover4.gno create mode 100644 gnovm/tests/files/closure17_recover6.gno create mode 100644 gnovm/tests/files/closure17_recover6a.gno create mode 100644 gnovm/tests/files/closure17_sort_search_efficiency.gno create mode 100644 gnovm/tests/files/closure17_zregexp_stdlibs.gno create mode 100644 gnovm/tests/files/closure9.gno create mode 100644 gnovm/tests/files/closure9_a0.gno create mode 100644 gnovm/tests/files/closure9_a02.gno create mode 100644 gnovm/tests/files/closure9_a1.gno create mode 100644 gnovm/tests/files/closure9_b.gno create mode 100644 gnovm/tests/files/closure9_c.gno create mode 100644 gnovm/tests/files/closure9_d.gno create mode 100644 gnovm/tests/files/closure9_e.gno create mode 100644 gnovm/tests/files/closure9_f.gno create mode 100644 gnovm/tests/files/closure9_g.gno create mode 100644 gnovm/tests/files/closure9_h.gno create mode 100644 gnovm/tests/files/closure9_h_0.gno create mode 100644 gnovm/tests/files/closure9_i.gno create mode 100644 gnovm/tests/files/closure9_j.gno rename a.gno => gnovm/tests/files/closure_9a.gno (84%) diff --git a/gnovm/tests/debug/3_break.gno b/gnovm/tests/debug/3_break.gno new file mode 100644 index 00000000000..0a2a2a5831f --- /dev/null +++ b/gnovm/tests/debug/3_break.gno @@ -0,0 +1,26 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + if i == 2 { + break + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 +// 3 diff --git a/gnovm/tests/debug/3_continue.gno b/gnovm/tests/debug/3_continue.gno new file mode 100644 index 00000000000..d0b8b48e7db --- /dev/null +++ b/gnovm/tests/debug/3_continue.gno @@ -0,0 +1,26 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + if i <= 2 { + continue + } + + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 4 +// 5 diff --git a/gnovm/tests/debug/3_goto.gno b/gnovm/tests/debug/3_goto.gno new file mode 100644 index 00000000000..3f8b29bfd66 --- /dev/null +++ b/gnovm/tests/debug/3_goto.gno @@ -0,0 +1,27 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + if i == 2 { + goto END + } + } +END: + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 +// 3 diff --git a/gnovm/tests/debug2/1.gno b/gnovm/tests/debug2/1.gno new file mode 100644 index 00000000000..47bf386f606 --- /dev/null +++ b/gnovm/tests/debug2/1.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 2 { + return + } + x := y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug2/1a.gnoa b/gnovm/tests/debug2/1a.gnoa new file mode 100644 index 00000000000..eaef398debe --- /dev/null +++ b/gnovm/tests/debug2/1a.gnoa @@ -0,0 +1,23 @@ +package main + +func main() { + var y int + var f []func() + + defer func() { + for _, ff := range f { + ff() + } + }() + + for i := 0; i < 3; i++ { + x := y + f = append(f, func() { println(x) }) + y++ + } +} + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/debug2/1a_goto.gno b/gnovm/tests/debug2/1a_goto.gno new file mode 100644 index 00000000000..903e611fc95 --- /dev/null +++ b/gnovm/tests/debug2/1a_goto.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 2 { + return + } + f = append(f, func() { println(y) }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug2/1b.gno b/gnovm/tests/debug2/1b.gno new file mode 100644 index 00000000000..cc934ed4bf1 --- /dev/null +++ b/gnovm/tests/debug2/1b.gno @@ -0,0 +1,34 @@ +package main + +// invalid goto +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 5 { + goto LABEL_2 + } + x := y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? + +LABEL_2: + println("end") +} + +// Output: +// end +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/1b_goto.gno b/gnovm/tests/debug2/1b_goto.gno new file mode 100644 index 00000000000..8e309758aa6 --- /dev/null +++ b/gnovm/tests/debug2/1b_goto.gno @@ -0,0 +1,36 @@ +package main + +func main() { + var y, counter int + + println(&y) + + var f []func() *int + + defer func() { + for _, ff := range f { + println(ff()) + println(*ff()) + } + }() + + // this is actually a implicit for loop +LABEL_1: + if counter == 2 { + return + } + f = append(f, func() *int { + //x := &y + return &y + }) + y++ + counter++ + goto LABEL_1 // this is the edge condition, break? continue? +} + +// Output: +// &0x14000283f80.(*int) +// &0x14000283f80.(*int) +// 1 +// &0x14000283f80.(*int) +// 2 diff --git a/gnovm/tests/debug2/2.gno b/gnovm/tests/debug2/2.gno new file mode 100644 index 00000000000..31d25cf6cb7 --- /dev/null +++ b/gnovm/tests/debug2/2.gno @@ -0,0 +1,34 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + //defer func() { + // for _, fn := range fns { + // println(fn()) + // } + //}() + + for i := 0; i < 5; i++ { + //defer func() { + // for _, fn := range fns { + // println(fn()) + // } + //}() + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/3.gno b/gnovm/tests/debug2/3.gno new file mode 100644 index 00000000000..4fffeb4fea0 --- /dev/null +++ b/gnovm/tests/debug2/3.gno @@ -0,0 +1,25 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + x += 1 + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 +// 3 +// 4 +// 5 diff --git a/gnovm/tests/debug2/4.gno b/gnovm/tests/debug2/4.gno new file mode 100644 index 00000000000..3835db7d114 --- /dev/null +++ b/gnovm/tests/debug2/4.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually a implicit for loop +FOO_LABEL: + if counter == 5 { + //for _, ff := range f { + // ff() + //} + return + } + x := y + f = append(f, func() { println(x) }) + x += 1 + y++ + counter++ + goto FOO_LABEL // this is the edge condition, break? continue? +} diff --git a/gnovm/tests/debug2/5.gno b/gnovm/tests/debug2/5.gno new file mode 100644 index 00000000000..d0989412170 --- /dev/null +++ b/gnovm/tests/debug2/5.gno @@ -0,0 +1,33 @@ +package main + +import "fmt" + +func main() { + var counter int + var fns []func() + + defer func() { + for _, f := range fns { + f() + } + }() + +LABEL_1: + if counter >= 2 { + goto LABEL_2 // Jump to LABEL_2 once counter is 2 or more + } + + // capture + fns = append(fns, func() { println(counter) }) + counter++ + goto LABEL_1 // Go back to the beginning of the loop + +LABEL_2: + fmt.Println("Exited loop with counter at:", counter) + // Continue with the rest of the program +} + +// Output: +// Exited loop with counter at: 2 +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure0.gno b/gnovm/tests/debug2/closure0.gno new file mode 100644 index 00000000000..acc1abfd404 --- /dev/null +++ b/gnovm/tests/debug2/closure0.gno @@ -0,0 +1,17 @@ +package main + +type adder func(int, int) int + +func genAdd(k int) adder { + return func(i, j int) int { + return i + j + k + } +} + +func main() { + f := genAdd(5) + println(f(3, 4)) +} + +// Output: +// 12 diff --git a/gnovm/tests/debug2/closure11.gno b/gnovm/tests/debug2/closure11.gno new file mode 100644 index 00000000000..1ea6e013b0d --- /dev/null +++ b/gnovm/tests/debug2/closure11.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug2/closure11_b.gno b/gnovm/tests/debug2/closure11_b.gno new file mode 100644 index 00000000000..427c6f075f9 --- /dev/null +++ b/gnovm/tests/debug2/closure11_b.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + if true { + x += y + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/debug2/closure12_a.gno b/gnovm/tests/debug2/closure12_a.gno new file mode 100644 index 00000000000..1e3eb9fe815 --- /dev/null +++ b/gnovm/tests/debug2/closure12_a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + for i := 0; i < 1; i++ { + x++ + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure15_a.gno b/gnovm/tests/debug2/closure15_a.gno new file mode 100644 index 00000000000..a30f558350e --- /dev/null +++ b/gnovm/tests/debug2/closure15_a.gno @@ -0,0 +1,50 @@ +package main + +import "fmt" + +// recursive closure does not capture +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 3; i++ { + recursiveFunc = func(num int) int { + x := i + println("value of x: ", x) + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } +} + +// Go Output: +// value of x: 3 +// Factorial of 0 is: 1 +// value of x: 3 +// value of x: 3 +// Factorial of 1 is: 1 +// value of x: 3 +// value of x: 3 +// value of x: 3 +// Factorial of 2 is: 2 + +// Output: +// value of x: 0 +// Factorial of 0 is: 1 +// value of x: 1 +// value of x: 2 +// Factorial of 1 is: 1 +// value of x: 2 +// value of x: 2 +// value of x: 2 +// Factorial of 2 is: 2 diff --git a/gnovm/tests/debug2/closure16.gno b/gnovm/tests/debug2/closure16.gno new file mode 100644 index 00000000000..8a472ffb410 --- /dev/null +++ b/gnovm/tests/debug2/closure16.gno @@ -0,0 +1,21 @@ +package main + +func main() { + var fns []func() int + s := []int{1, 2, 3} + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure17_sort_search_efficiency.gno b/gnovm/tests/debug2/closure17_sort_search_efficiency.gno new file mode 100644 index 00000000000..90362944ac5 --- /dev/null +++ b/gnovm/tests/debug2/closure17_sort_search_efficiency.gno @@ -0,0 +1,21 @@ +package main + +func Search(n int, f func(int) bool) int { + f(1) + return 0 +} + +func main() { + for x := 0; x < 2; x++ { + count := 0 + println(" first: count: ", count) + Search(1, func(i int) bool { count++; return i >= x }) + println("second: count: ", count) + } +} + +// Output: +// first: count: 0 +// second: count: 1 +// first: count: 0 +// second: count: 1 diff --git a/gnovm/tests/debug2/closure9.gno b/gnovm/tests/debug2/closure9.gno new file mode 100644 index 00000000000..0de5fc8292e --- /dev/null +++ b/gnovm/tests/debug2/closure9.gno @@ -0,0 +1,26 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + for i := 0; i < 3; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 2 + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure9_g.gno b/gnovm/tests/debug2/closure9_g.gno new file mode 100644 index 00000000000..bd8acbe02a0 --- /dev/null +++ b/gnovm/tests/debug2/closure9_g.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + { // another block + f := func() int { + return x + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/debug2/closure9_h.gno b/gnovm/tests/debug2/closure9_h.gno new file mode 100644 index 00000000000..23cb5ff9f40 --- /dev/null +++ b/gnovm/tests/debug2/closure9_h.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 1 +// 2 + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/debug2/closure9_i.gno b/gnovm/tests/debug2/closure9_i.gno new file mode 100644 index 00000000000..42a4d11d862 --- /dev/null +++ b/gnovm/tests/debug2/closure9_i.gno @@ -0,0 +1,31 @@ +package main + +func main() { + var fns []func() int + var x int + for i := 0; i < 2; i++ { + x = i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 1 +// 2 +// 1 +// 2 + +// Output: +// 1 +// 2 +// 1 +// 2 diff --git a/gnovm/tests/debug2/declare.gno b/gnovm/tests/debug2/declare.gno new file mode 100644 index 00000000000..761c3029a16 --- /dev/null +++ b/gnovm/tests/debug2/declare.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(a) + a := 0 +} + +// Output: diff --git a/gnovm/tests/debug2/defer.gno b/gnovm/tests/debug2/defer.gno new file mode 100644 index 00000000000..c724840cd23 --- /dev/null +++ b/gnovm/tests/debug2/defer.gno @@ -0,0 +1,41 @@ +package main + +// explain: +// in the 1st loop, fns has a fn capture x with value 0, +// and a defer func is placed with refs to fns(it's not captured in face), +// in the 2nd loop, fn captured x with value 1, and fn appended to fns. +// so here, the first defer will print, 0 , 1. +// still in the 2nd loop, a second defer is placed, with fn[0] and fn[1] +// both capture the x in context, whose value is 1, so the second defer +// will print 1, 1 +func main() { + var fns []func() int + + println("start for loop") + for i := 0; i < 2; i++ { + defer func() { // possible resource leak? + println("defer") + for _, fn := range fns { + println(fn()) + } + }() + + x := i + f := func() int { + return x + } + + fns = append(fns, f) + } + println("end for loop") +} + +// Output: +// start for loop +// end for loop +// defer +// 0 +// 1 +// defer +// 0 +// 1 diff --git a/gnovm/tests/debug2/foo1155.gno b/gnovm/tests/debug2/foo1155.gno new file mode 100644 index 00000000000..bd3d5e16a3b --- /dev/null +++ b/gnovm/tests/debug2/foo1155.gno @@ -0,0 +1,25 @@ +package main + +func BalanceOf(i int) int { + return 100 +} + +func main() { + bar := 100 + for i, tc := range []struct { + name string + expected interface{} + fn func() interface{} + }{ + {"BalanceOf(admin, tid1)", 100, func() interface{} { return BalanceOf(bar) }}, + } { + println("hey") + println(i) + println(tc) + } +} + +// Output: +// hey +// 0 +// struct{("BalanceOf(admin, tid1)" string),(100 int),( func()( interface{}))} diff --git a/gnovm/tests/debug2/inc.gno b/gnovm/tests/debug2/inc.gno new file mode 100644 index 00000000000..b45aa0569e3 --- /dev/null +++ b/gnovm/tests/debug2/inc.gno @@ -0,0 +1,10 @@ +package main + +func main() { + x := 1 + x++ + println(x) +} + +// Output: +// 2 diff --git a/gnovm/tests/debug2/return.gno b/gnovm/tests/debug2/return.gno new file mode 100644 index 00000000000..6435588e080 --- /dev/null +++ b/gnovm/tests/debug2/return.gno @@ -0,0 +1,9 @@ +package main + +func main() { + a := 0 + return + println(a) +} + +// Output: diff --git a/gnovm/tests/debug2/shadow.gno b/gnovm/tests/debug2/shadow.gno new file mode 100644 index 00000000000..1b2d40eacdf --- /dev/null +++ b/gnovm/tests/debug2/shadow.gno @@ -0,0 +1,16 @@ +package main + +func main() { + var x int + + if true { + x += 2 + println("inner x: ", x) + } + + println("outer x:", x) +} + +// Output: +// inner x: 2 +// outer x: 2 diff --git a/gnovm/tests/debug2/sort_search_exhausted.gno b/gnovm/tests/debug2/sort_search_exhausted.gno new file mode 100644 index 00000000000..e44a9bf0967 --- /dev/null +++ b/gnovm/tests/debug2/sort_search_exhausted.gno @@ -0,0 +1,20 @@ +package main + +func Search(n int, f func(int) bool) int { + i, j := 0, n + for i := 0; i < j; i++ { + h := int(uint(i+j) >> 1) // avoid overflow when computing h + f(h) + } + return i +} + +func main() { + for size := 0; size < 2; size++ { + for targ := 0; targ <= size; targ++ { + Search(size, func(i int) bool { return i >= targ }) + } + } +} + +// Output: diff --git a/gnovm/tests/debug2/zrealm_avl1.gno b/gnovm/tests/debug2/zrealm_avl1.gno new file mode 100644 index 00000000000..462ff521447 --- /dev/null +++ b/gnovm/tests/debug2/zrealm_avl1.gno @@ -0,0 +1,379 @@ +// PKGPATH: gno.land/r/test +package main + +import ( + "gno.land/p/demo/avl" +) + +var node *avl.Node + +func init() { + node = avl.NewNode("key0", "value0") + node, _ = node.Set("key1", "value1") +} + +func main() { + var updated bool + node, updated = node.Set("key2", "value2") + // println(node, updated) + println(updated, node.Size()) +} + +// Output: +// false 3 + +// Realm: +// switchrealm["gno.land/r/test"] +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:9]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "value2" +// } +// }, +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:8]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key2" +// } +// }, +// {}, +// { +// "N": "AQAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "7a8a63e17a567d7b0891ac89d5cd90072a73787d", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:6" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "ab5a297f4eb033d88bdf1677f4dc151ccb9fde9f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:9" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "RefCount": "1" +// } +// } +// c[a8ada09dee16d791fd406d629fe29bb0ed084a30:7]={ +// "Fields": [ +// { +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// }, +// "V": { +// "@type": "/gno.StringValue", +// "value": "key1" +// } +// }, +// {}, +// { +// "N": "AgAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "64" +// } +// }, +// { +// "N": "AwAAAAAAAAA=", +// "T": { +// "@type": "/gno.PrimitiveType", +// "value": "32" +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "627e8e517e7ae5db0f3b753e2a32b607989198b6", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:5" +// } +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "fe8afd501233fb95375016199f0443b3c6ab1fbc", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:8" +// } +// } +// } +// } +// ], +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7", +// "ModTime": "0", +// "OwnerID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "RefCount": "1" +// } +// } +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "6", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Values": [ +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "TransientLoopData": null, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "15", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "TransientLoopData": null, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.PointerType", +// "Elt": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// } +// }, +// "V": { +// "@type": "/gno.PointerValue", +// "Base": null, +// "Index": "0", +// "TV": { +// "T": { +// "@type": "/gno.RefType", +// "ID": "gno.land/p/demo/avl.Node" +// }, +// "V": { +// "@type": "/gno.RefValue", +// "Hash": "c5eefc40ed065461b4a920c1349ed734ffdead8f", +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:7" +// } +// } +// } +// } +// ] +// } +// d[a8ada09dee16d791fd406d629fe29bb0ed084a30:4] diff --git a/gnovm/tests/file_test.go b/gnovm/tests/file_test.go index f070546ab74..10f7c19fef4 100644 --- a/gnovm/tests/file_test.go +++ b/gnovm/tests/file_test.go @@ -32,6 +32,13 @@ func TestFiles(t *testing.T) { runFileTests(t, baseDir, []string{"*_native*"}) } +func TestDebug(t *testing.T) { + // baseDir := filepath.Join(".", "debug2") + baseDir := filepath.Join(".", "debug") + runFileTests(t, baseDir, []string{"*_native*"}) + // runFileTests(t, baseDir, []string{"*_stdlibs*"}, WithNativeLibs()) +} + func TestChallenges(t *testing.T) { baseDir := filepath.Join(".", "challenges") runFileTests(t, baseDir, nil) diff --git a/gnovm/tests/files/append5.gno b/gnovm/tests/files/append5.gno index 0eba5a46463..b1fdae852b1 100644 --- a/gnovm/tests/files/append5.gno +++ b/gnovm/tests/files/append5.gno @@ -7,4 +7,4 @@ func main() { } // Output: -// X \ No newline at end of file +// X diff --git a/gnovm/tests/files/closure10.gno b/gnovm/tests/files/closure10.gno new file mode 100644 index 00000000000..1a788d9e0b2 --- /dev/null +++ b/gnovm/tests/files/closure10.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + //x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 1 diff --git a/gnovm/tests/files/closure10_a.gno b/gnovm/tests/files/closure10_a.gno new file mode 100644 index 00000000000..45ad06c6945 --- /dev/null +++ b/gnovm/tests/files/closure10_a.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func bar() func() func() int { + x := 1 + + // First level of closure, modifies x + return func() func() int { + x++ + // Second level of closure, returns x + return func() int { + return x + } + } +} + +func main() { + f := bar() // f is the first-level closure + g := f() // g is the second-level closure, x is incremented here + + fmt.Println(g()) // prints the value of x after being modified by the first-level closure +} + +// Output: +// 2 diff --git a/gnovm/tests/files/closure11.gno b/gnovm/tests/files/closure11.gno new file mode 100644 index 00000000000..1ea6e013b0d --- /dev/null +++ b/gnovm/tests/files/closure11.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure11_a.gno b/gnovm/tests/files/closure11_a.gno new file mode 100644 index 00000000000..2ce3df96097 --- /dev/null +++ b/gnovm/tests/files/closure11_a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + if true { + return x + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure11_b.gno b/gnovm/tests/files/closure11_b.gno new file mode 100644 index 00000000000..427c6f075f9 --- /dev/null +++ b/gnovm/tests/files/closure11_b.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + if true { + x += y + if true { + return x + } + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure12.gno b/gnovm/tests/files/closure12.gno new file mode 100644 index 00000000000..44717e05cc0 --- /dev/null +++ b/gnovm/tests/files/closure12.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + { + return x + } + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure12_a.gno b/gnovm/tests/files/closure12_a.gno new file mode 100644 index 00000000000..1e3eb9fe815 --- /dev/null +++ b/gnovm/tests/files/closure12_a.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + f := func() int { + for i := 0; i < 1; i++ { + x++ + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure12_b.gno b/gnovm/tests/files/closure12_b.gno new file mode 100644 index 00000000000..4351ceaaf49 --- /dev/null +++ b/gnovm/tests/files/closure12_b.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + s := []int{1, 2} + f := func() int { + for _, v := range s { + x += v + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 3 +// 4 diff --git a/gnovm/tests/files/closure12_c.gno b/gnovm/tests/files/closure12_c.gno new file mode 100644 index 00000000000..3e42e207649 --- /dev/null +++ b/gnovm/tests/files/closure12_c.gno @@ -0,0 +1,27 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 2; i++ { + x := i + y := 1 + f := func() int { + switch y { + case 1: + x += 1 + default: + x += 0 + } + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure12_e.gno b/gnovm/tests/files/closure12_e.gno new file mode 100644 index 00000000000..17490510b26 --- /dev/null +++ b/gnovm/tests/files/closure12_e.gno @@ -0,0 +1,27 @@ +package main + +type queueOnePass struct { + sparse []uint32 + dense []uint32 + size, nextIndex uint32 +} + +func newQueue(size int) (q *queueOnePass) { + return &queueOnePass{ + sparse: make([]uint32, size), + dense: make([]uint32, size), + } +} +func main() { + var ( + visitQueue = newQueue(10) + ) + f := func() { + println(visitQueue.size) + } + + f() +} + +// Output: +// 0 diff --git a/gnovm/tests/files/closure12_f.gno b/gnovm/tests/files/closure12_f.gno new file mode 100644 index 00000000000..60e327bd2b2 --- /dev/null +++ b/gnovm/tests/files/closure12_f.gno @@ -0,0 +1,20 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + for i, v := range s { // TODO: exclude s, it's final + println(i) + println(v) + } + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/closure12_g.gno b/gnovm/tests/files/closure12_g.gno new file mode 100644 index 00000000000..bf998c163d8 --- /dev/null +++ b/gnovm/tests/files/closure12_g.gno @@ -0,0 +1,14 @@ +package main + +func main() { + f := func(a int) bool { + println(a) + return true + } + + println(f(5)) +} + +// Output: +// 5 +// true diff --git a/gnovm/tests/files/closure12_h.gno b/gnovm/tests/files/closure12_h.gno new file mode 100644 index 00000000000..c9f1f6e97e8 --- /dev/null +++ b/gnovm/tests/files/closure12_h.gno @@ -0,0 +1,25 @@ +package main + +func main() { + s := 1 + f := func() { + i := 0 // no capture for i + var j = s // s should be captured, j not + k := s // s should be captured, k not + m, n := s, 0 + println(i) + println(j) + println(k) + println(m) + println(n) + } + + f() +} + +// Output: +// 0 +// 1 +// 1 +// 1 +// 0 diff --git a/gnovm/tests/files/closure12_i.gno b/gnovm/tests/files/closure12_i.gno new file mode 100644 index 00000000000..acf1b53b32a --- /dev/null +++ b/gnovm/tests/files/closure12_i.gno @@ -0,0 +1,15 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + if len(s) == 2 { + println("ok") + } + } + f() +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure13.gno b/gnovm/tests/files/closure13.gno new file mode 100644 index 00000000000..ece19dd87f6 --- /dev/null +++ b/gnovm/tests/files/closure13.gno @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + // Define a function that returns a closure + var recursiveFunc func(int) int + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + // Closure calling itself recursively + return num * recursiveFunc(num-1) + } + + // Use the recursive closure + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/files/closure13_a.gno b/gnovm/tests/files/closure13_a.gno new file mode 100644 index 00000000000..8345c9ed48d --- /dev/null +++ b/gnovm/tests/files/closure13_a.gno @@ -0,0 +1,24 @@ +package main + +import "fmt" + +func main() { + var recursiveFunc func(int) int + var recursiveFunc2 func(int) int + + recursiveFunc = func(num int) int { + recursiveFunc2 = recursiveFunc + + if num <= 0 { + return 1 + } + + return num * recursiveFunc2(num-1) + } + + result := recursiveFunc(5) + fmt.Println("Factorial of 5 is:", result) // Output: 120 +} + +// Output: +// Factorial of 5 is: 120 diff --git a/gnovm/tests/files/closure13_b0.gno b/gnovm/tests/files/closure13_b0.gno new file mode 100644 index 00000000000..4bfe864cc8e --- /dev/null +++ b/gnovm/tests/files/closure13_b0.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, err error) { + b := buf[0:size] + println(b) + println(len(buf)) + println(cap(buf)) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() { + buf = make([]byte, 20) + expectRead(5, "foo ", nil) + } + withFooBar() +} + +// Output: +// slice[0x0000000000] +// 20 +// 20 diff --git a/gnovm/tests/files/closure13_b1.gno b/gnovm/tests/files/closure13_b1.gno new file mode 100644 index 00000000000..9218ee16fa3 --- /dev/null +++ b/gnovm/tests/files/closure13_b1.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var buf []byte + + // when eval this, buf is still nil, + expectRead := func(size int, expected string, eerr error) { + b := buf[0:size] + println(b) + } + + // buf should be captured here, here it's volatile, not where it defined + // namely, the vp should point here, -> so mutate offset + withFooBar := func() func() { + buf = make([]byte, 20) + return func() { + b := buf[0:4] + println(b) + } + } + withFooBar() + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure13_b_stdlibs.gno b/gnovm/tests/files/closure13_b_stdlibs.gno new file mode 100644 index 00000000000..7d9d935652c --- /dev/null +++ b/gnovm/tests/files/closure13_b_stdlibs.gno @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "io" + "strings" +) + +func main() { + var mr io.Reader + var buf []byte + nread := 0 + withFooBar := func(tests func()) { + r1 := strings.NewReader("foo ") + //r2 := strings.NewReader("") + //r3 := strings.NewReader("bar") + //mr = io.MultiReader(r1, r2, r3) + mr = io.MultiReader(r1) + buf = make([]byte, 20) + tests() + } + expectRead := func(size int, expected string, eerr error) { + nread++ + n, gerr := mr.Read(buf[0:size]) + if n != len(expected) { + panic(fmt.Sprintf("#%d, expected %d bytes; got %d", + nread, len(expected), n)) + } + got := string(buf[0:n]) + if got != expected { + panic(fmt.Sprintf("#%d, expected %q; got %q", + nread, expected, got)) + } + if gerr != eerr { + panic(fmt.Sprintf("#%d, expected error %v; got %v", + nread, eerr, gerr)) + } + buf = buf[n:] + } + withFooBar(func() { + expectRead(5, "foo ", nil) + }) + println("ok") +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure14.gno b/gnovm/tests/files/closure14.gno new file mode 100644 index 00000000000..e6b89f9528a --- /dev/null +++ b/gnovm/tests/files/closure14.gno @@ -0,0 +1,26 @@ +package main + +import "fmt" + +func foo() (err error) { + defer func() { + if r := recover(); r != nil { + switch v := r.(type) { + case error: + err = v + default: + err = fmt.Errorf("%s", v) + } + } + }() + + panic("xxx") +} + +func main() { + err := foo() + println(err.Error()) +} + +// Output: +// xxx diff --git a/gnovm/tests/files/closure14_a.gno b/gnovm/tests/files/closure14_a.gno new file mode 100644 index 00000000000..706e2bd7e8b --- /dev/null +++ b/gnovm/tests/files/closure14_a.gno @@ -0,0 +1,27 @@ +package main + +import ( + "errors" +) + +func foo() (err error) { + y := 1 + defer func() { + if r := recover(); r != nil { + switch y { + case 1: + err = errors.New("ok") + default: + err = nil + } + } + }() + panic(y) +} + +func main() { + println(foo()) +} + +// Output: +// ok diff --git a/gnovm/tests/files/closure15.gno b/gnovm/tests/files/closure15.gno new file mode 100644 index 00000000000..86c8fc445f0 --- /dev/null +++ b/gnovm/tests/files/closure15.gno @@ -0,0 +1,32 @@ +package main + +import "fmt" + +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 6; i++ { + recursiveFunc = func(num int) int { + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } +} + +// Output: +// Factorial of 0 is: 1 +// Factorial of 1 is: 1 +// Factorial of 2 is: 2 +// Factorial of 3 is: 6 +// Factorial of 4 is: 24 +// Factorial of 5 is: 120 diff --git a/gnovm/tests/files/closure15_a.gno b/gnovm/tests/files/closure15_a.gno new file mode 100644 index 00000000000..1983845bd81 --- /dev/null +++ b/gnovm/tests/files/closure15_a.gno @@ -0,0 +1,49 @@ +package main + +import "fmt" + +// recursive closure does not capture +func main() { + + var fns []func(int) int + var recursiveFunc func(int) int + + for i := 0; i < 3; i++ { + recursiveFunc = func(num int) int { + x := i + println("value of x: ", x) + if num <= 0 { + return 1 + } + return num * recursiveFunc(num-1) + } + fns = append(fns, recursiveFunc) + } + + for i, r := range fns { + result := r(i) + fmt.Printf("Factorial of %d is: %d \n", i, result) + } +} + +// Go Output: +// value of x: 3 +// Factorial of 0 is: 1 +// value of x: 3 +// value of x: 3 +// Factorial of 1 is: 1 +// value of x: 3 +// value of x: 3 +// value of x: 3 +// Factorial of 2 is: 2 + +// Output: +// value of x: 0 +// Factorial of 0 is: 1 +// value of x: 1 +// value of x: 2 +// Factorial of 1 is: 1 +// value of x: 2 +// value of x: 2 +// value of x: 2 +// Factorial of 2 is: 2 diff --git a/gnovm/tests/files/closure16.gno b/gnovm/tests/files/closure16.gno new file mode 100644 index 00000000000..8a472ffb410 --- /dev/null +++ b/gnovm/tests/files/closure16.gno @@ -0,0 +1,21 @@ +package main + +func main() { + var fns []func() int + s := []int{1, 2, 3} + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/closure16_a.gno b/gnovm/tests/files/closure16_a.gno new file mode 100644 index 00000000000..ee375bc719c --- /dev/null +++ b/gnovm/tests/files/closure16_a.gno @@ -0,0 +1,20 @@ +package main + +func main() { + var fns []func() int + m := map[string]int{"a": 1, "b": 2} + for _, v := range m { + x := v + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure16_a1.gno b/gnovm/tests/files/closure16_a1.gno new file mode 100644 index 00000000000..857cc11d7da --- /dev/null +++ b/gnovm/tests/files/closure16_a1.gno @@ -0,0 +1,19 @@ +package main + +func main() { + var fns []func() int + m := map[string]int{"a": 1, "b": 2} + for _, v := range m { + f := func() int { + return v + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure16_b.gno b/gnovm/tests/files/closure16_b.gno new file mode 100644 index 00000000000..5d26a2c6dc0 --- /dev/null +++ b/gnovm/tests/files/closure16_b.gno @@ -0,0 +1,23 @@ +package main + +func main() { + var fns []func() int + s := "hello" + for i, _ := range s { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure16_b1.gno b/gnovm/tests/files/closure16_b1.gno new file mode 100644 index 00000000000..a7ddb99504f --- /dev/null +++ b/gnovm/tests/files/closure16_b1.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + s := "hello" + for i, _ := range s { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure17_io2.gno b/gnovm/tests/files/closure17_io2.gno new file mode 100644 index 00000000000..24655f5040c --- /dev/null +++ b/gnovm/tests/files/closure17_io2.gno @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "io" + "log" + "strings" +) + +func main() { + r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") + + b, err := io.ReadAll(r) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s", b) +} + +// Output: +// Go is a general-purpose language designed with systems programming in mind. diff --git a/gnovm/tests/files/closure17_recover4.gno b/gnovm/tests/files/closure17_recover4.gno new file mode 100644 index 00000000000..5a6da4261a2 --- /dev/null +++ b/gnovm/tests/files/closure17_recover4.gno @@ -0,0 +1,25 @@ +package main + +import "fmt" + +func div(a, b int) (result int) { + defer func() { + r := recover() + + fmt.Printf("r = %#v\n", r) + + if r != nil { + result = 0 + } + }() + + return a / b +} + +func main() { + println(div(30, 2)) +} + +// Output: +// r = +// 15 diff --git a/gnovm/tests/files/closure17_recover6.gno b/gnovm/tests/files/closure17_recover6.gno new file mode 100644 index 00000000000..0b304369764 --- /dev/null +++ b/gnovm/tests/files/closure17_recover6.gno @@ -0,0 +1,30 @@ +package main + +import ( + "errors" +) + +func main() { + println(f(false)) + println(f(true)) +} + +func f(dopanic bool) (err error) { + defer func() { + if x := recover(); x != nil { + err = x.(error) + } + }() + q(dopanic) + return +} + +func q(dopanic bool) { + if dopanic { + panic(errors.New("wtf")) + } +} + +// Output: +// undefined +// wtf diff --git a/gnovm/tests/files/closure17_recover6a.gno b/gnovm/tests/files/closure17_recover6a.gno new file mode 100644 index 00000000000..427fc4b74b9 --- /dev/null +++ b/gnovm/tests/files/closure17_recover6a.gno @@ -0,0 +1,40 @@ +package main + +import "errors" + +//func p(s interface{}) { +// fmt.Printf("%T \n", s) +// if v, ok := s.(string); ok { +// println(v) +// panic(v) +// } else { +// println("---") +// } +//} + +type error interface { + Error() string +} + +// New returns an error that formats as the given text. +// Each call to New returns a distinct error value even if the text is identical. +func New(text string) error { + return &errorString{text} +} + +// errorString is a trivial implementation of error. +type errorString struct { + s string +} + +func (e *errorString) Error() string { + return e.s +} + +func main() { + //panic(New("wtf")) + panic(errors.New("wtf")) +} + +// Error: +// wtf diff --git a/gnovm/tests/files/closure17_sort_search_efficiency.gno b/gnovm/tests/files/closure17_sort_search_efficiency.gno new file mode 100644 index 00000000000..90362944ac5 --- /dev/null +++ b/gnovm/tests/files/closure17_sort_search_efficiency.gno @@ -0,0 +1,21 @@ +package main + +func Search(n int, f func(int) bool) int { + f(1) + return 0 +} + +func main() { + for x := 0; x < 2; x++ { + count := 0 + println(" first: count: ", count) + Search(1, func(i int) bool { count++; return i >= x }) + println("second: count: ", count) + } +} + +// Output: +// first: count: 0 +// second: count: 1 +// first: count: 0 +// second: count: 1 diff --git a/gnovm/tests/files/closure17_zregexp_stdlibs.gno b/gnovm/tests/files/closure17_zregexp_stdlibs.gno new file mode 100644 index 00000000000..10bb6f937d3 --- /dev/null +++ b/gnovm/tests/files/closure17_zregexp_stdlibs.gno @@ -0,0 +1,19 @@ +// MAXALLOC: 100000000 +// max total allocation of 100 mb. +package main + +import "regexp" + +var reName = regexp.MustCompile(`^[a-z]+[_a-z0-9]*$`) + +func main() { + for j := 0; j < 100; j++ { + if !(reName.MatchString("thisisatestname")) { + panic("error") + } + } + println(true) +} + +// Output: +// true diff --git a/gnovm/tests/files/closure9.gno b/gnovm/tests/files/closure9.gno new file mode 100644 index 00000000000..e3a4917211f --- /dev/null +++ b/gnovm/tests/files/closure9.gno @@ -0,0 +1,30 @@ +package main + +// this is a fix, intuitive, and same with Go +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 2 +// 3 +// 4 + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_a0.gno b/gnovm/tests/files/closure9_a0.gno new file mode 100644 index 00000000000..0f5ba6d6739 --- /dev/null +++ b/gnovm/tests/files/closure9_a0.gno @@ -0,0 +1,29 @@ +package main + +// this is a fix, intuitive, same with go loopVar experiment feature. +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 5 +// 5 +// 5 +// 5 +// 5 + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_a02.gno b/gnovm/tests/files/closure9_a02.gno new file mode 100644 index 00000000000..82ca810b13c --- /dev/null +++ b/gnovm/tests/files/closure9_a02.gno @@ -0,0 +1,20 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + return x + } + fns = append(fns, f) + } + println(fns[3]()) + println(fns[2]()) + println(fns[0]()) +} + +// Output: +// 3 +// 2 +// 0 diff --git a/gnovm/tests/files/closure9_a1.gno b/gnovm/tests/files/closure9_a1.gno new file mode 100644 index 00000000000..19951610e7e --- /dev/null +++ b/gnovm/tests/files/closure9_a1.gno @@ -0,0 +1,34 @@ +package main + +// this still keeps consistent with go, that a variable out of loop block is not captured +// this is unintuitive(should capture something) +// TODO: leave it for discuss. + +func main() { + var fns []func() int + var x int + for i := 0; i < 5; i++ { + x = i + f := func() int { + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 4 +// 4 +// 4 +// 4 +// 4 + +// Output: +// 4 +// 4 +// 4 +// 4 +// 4 diff --git a/gnovm/tests/files/closure9_b.gno b/gnovm/tests/files/closure9_b.gno new file mode 100644 index 00000000000..0822368fe3a --- /dev/null +++ b/gnovm/tests/files/closure9_b.gno @@ -0,0 +1,22 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + x += 1 + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_c.gno b/gnovm/tests/files/closure9_c.gno new file mode 100644 index 00000000000..e5c61c59089 --- /dev/null +++ b/gnovm/tests/files/closure9_c.gno @@ -0,0 +1,18 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + f := func() int { + return i + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/closure9_d.gno b/gnovm/tests/files/closure9_d.gno new file mode 100644 index 00000000000..185961c741f --- /dev/null +++ b/gnovm/tests/files/closure9_d.gno @@ -0,0 +1,29 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + f := func() int { + x := 5 + return x + } + println(x) + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 5 +// 5 +// 5 +// 5 diff --git a/gnovm/tests/files/closure9_e.gno b/gnovm/tests/files/closure9_e.gno new file mode 100644 index 00000000000..b3b2b2b0385 --- /dev/null +++ b/gnovm/tests/files/closure9_e.gno @@ -0,0 +1,12 @@ +package main + +func main() { + for i := 0; i < 5; i++ { + if i == 1 { + panic("error 1") + } + } +} + +// Error: +// error 1 diff --git a/gnovm/tests/files/closure9_f.gno b/gnovm/tests/files/closure9_f.gno new file mode 100644 index 00000000000..19722ec3ae6 --- /dev/null +++ b/gnovm/tests/files/closure9_f.gno @@ -0,0 +1,53 @@ +package main + +// 1. we can determine target vars by hasClosure pattern and externNames(without uverse). +// it can be a loopvar, or vars derived from loopvar, e.g. x := i; +// 2. now we need to capture every transient state of the target var; firstly we should +// eval the transient state, there are two ways around: +// a) eval funcLit, but the time doing this is hard to determine, namely, you have to eval +// before it leaves its using context. In some sense, you have to cover all cases when a funcValue +// is used, like assign, xxxLit, funcCall as an arg, etc. +// b) another way for this is to generate a `time series` values that the index is the loop index, +// e.g. in a for loop, we define a new var like x := i, we store the transient state of x per loop +// in the time series, which is a slice. This slice is used when the closure fv is called, +// replacing the var in default block.(func value have a slice of name indicates it's captured, when +// eval name in this slice, using the time series instead). +// this solution mainly differs that it eval target x independently with the closure funcLit, so it avoids +// the obsession to determine the eval order. this seems the right way. hmmm. + +//======================================================================================================== +// work flow 1.0, hard to do +// 1. determine loop var, whole logic depends on this, the key word is dynamic, it causes polymorphic. +// this should most be done via define(), // TODO: check it +// 2. in transcribe, find if loopvar is used somewhere, to identify pattern like x := i, what to do with +// other use cases? this brings complexity. This is wrong feel. +// we can generate the ts slice for every target var, e.g. x in this case. if two vars, two slices. so fv should +// reference a slice of slice. + +// work flow 2.0 +// 1. ignore loop var, assume all externNames apart from uverse is target captured var, this also needs to happen +// in transcribe, tag it, and can be checked everywhere it's appears in runtime, x := i, or var x int, etc. +// when eval funcLit, new a slice of slice and push value for further update. + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + //x := i // check if x is polymorphic, if yes, new a slice and set value by index + var x int // new a slice no init, this would be updated later, so the slice can be mutated after opFuncLit + f := func() int { + return x // set the slice to fv in + } + x = i // check if x is polymorphic, is yes, update slice assigned to fv before this, which is a reference. + fns = append(fns, f) // where should eval funcLit, where it assigned somewhere, or used in another func lit + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_g.gno b/gnovm/tests/files/closure9_g.gno new file mode 100644 index 00000000000..e7a6b64f1f3 --- /dev/null +++ b/gnovm/tests/files/closure9_g.gno @@ -0,0 +1,24 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + x := i + { + f := func() int { + return x + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/closure9_h.gno b/gnovm/tests/files/closure9_h.gno new file mode 100644 index 00000000000..23cb5ff9f40 --- /dev/null +++ b/gnovm/tests/files/closure9_h.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 0 +// 1 +// 1 +// 2 + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_h_0.gno b/gnovm/tests/files/closure9_h_0.gno new file mode 100644 index 00000000000..d60fa8515a9 --- /dev/null +++ b/gnovm/tests/files/closure9_h_0.gno @@ -0,0 +1,28 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + for j := 0; j < 2; j++ { + f := func() int { + return i + j + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 4 +// 4 +// 4 +// 4 + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_i.gno b/gnovm/tests/files/closure9_i.gno new file mode 100644 index 00000000000..42a4d11d862 --- /dev/null +++ b/gnovm/tests/files/closure9_i.gno @@ -0,0 +1,31 @@ +package main + +func main() { + var fns []func() int + var x int + for i := 0; i < 2; i++ { + x = i + for j := 0; j < 2; j++ { + y := j + f := func() int { + return x + y + } + fns = append(fns, f) + } + } + for _, fn := range fns { + println(fn()) + } +} + +// Go Output: +// 1 +// 2 +// 1 +// 2 + +// Output: +// 1 +// 2 +// 1 +// 2 diff --git a/gnovm/tests/files/closure9_j.gno b/gnovm/tests/files/closure9_j.gno new file mode 100644 index 00000000000..926caf5090c --- /dev/null +++ b/gnovm/tests/files/closure9_j.gno @@ -0,0 +1,21 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 2; i++ { + x := i + y := 0 + f := func() int { + x += y + return x + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Output: +// 0 +// 1 diff --git a/a.gno b/gnovm/tests/files/closure_9a.gno similarity index 84% rename from a.gno rename to gnovm/tests/files/closure_9a.gno index 499b2aedd93..3bfa5f181d0 100644 --- a/a.gno +++ b/gnovm/tests/files/closure_9a.gno @@ -10,3 +10,8 @@ func main() { fn() } } + +// Output: +// 101 +// 202 +// 303