From c5dcb0aad91f134653dda8621910a0a2b2769acc Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Tue, 29 Oct 2024 01:59:48 +0100 Subject: [PATCH 01/11] 2973: fix switch branch stmts --- gnovm/pkg/gnolang/nodes.go | 21 +++++++------ gnovm/pkg/gnolang/op_exec.go | 16 ++-------- gnovm/pkg/gnolang/preprocess.go | 55 ++++++++++++++++++++++++++------- gnovm/tests/files/for21.gno | 17 ++++++++++ gnovm/tests/files/for22.gno | 17 ++++++++++ gnovm/tests/files/for23.gno | 17 ++++++++++ 6 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 gnovm/tests/files/for21.gno create mode 100644 gnovm/tests/files/for22.gno create mode 100644 gnovm/tests/files/for23.gno diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index c282b619fdc..45062f8e14c 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -149,16 +149,17 @@ func (loc Location) IsZero() bool { type GnoAttribute string const ( - ATTR_PREPROCESSED GnoAttribute = "ATTR_PREPROCESSED" - ATTR_PREDEFINED GnoAttribute = "ATTR_PREDEFINED" - ATTR_TYPE_VALUE GnoAttribute = "ATTR_TYPE_VALUE" - ATTR_TYPEOF_VALUE GnoAttribute = "ATTR_TYPEOF_VALUE" - ATTR_IOTA GnoAttribute = "ATTR_IOTA" - ATTR_LOCATIONED GnoAttribute = "ATTR_LOCATIONE" // XXX DELETE - ATTR_GOTOLOOP_STMT GnoAttribute = "ATTR_GOTOLOOP_STMT" // XXX delete? - ATTR_LOOP_DEFINES GnoAttribute = "ATTR_LOOP_DEFINES" // []Name defined within loops. - ATTR_LOOP_USES GnoAttribute = "ATTR_LOOP_USES" // []Name loop defines actually used. - ATTR_SHIFT_RHS GnoAttribute = "ATTR_SHIFT_RHS" + ATTR_PREPROCESSED GnoAttribute = "ATTR_PREPROCESSED" + ATTR_PREDEFINED GnoAttribute = "ATTR_PREDEFINED" + ATTR_TYPE_VALUE GnoAttribute = "ATTR_TYPE_VALUE" + ATTR_TYPEOF_VALUE GnoAttribute = "ATTR_TYPEOF_VALUE" + ATTR_IOTA GnoAttribute = "ATTR_IOTA" + ATTR_LOCATIONED GnoAttribute = "ATTR_LOCATIONE" // XXX DELETE + ATTR_GOTOLOOP_STMT GnoAttribute = "ATTR_GOTOLOOP_STMT" // XXX delete? + ATTR_LOOP_DEFINES GnoAttribute = "ATTR_LOOP_DEFINES" // []Name defined within loops. + ATTR_LOOP_USES GnoAttribute = "ATTR_LOOP_USES" // []Name loop defines actually used. + ATTR_SHIFT_RHS GnoAttribute = "ATTR_SHIFT_RHS" + ATTR_LAST_BLOCK_STMT GnoAttribute = "ATTR_LAST_BLOCK_STMT" ) type Attributes struct { diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index a61349b0806..900b5f8e9bb 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -676,25 +676,15 @@ EXEC_SWITCH: bs.Active = bs.Body[cs.BodyIndex] // prefill case FALLTHROUGH: ss, ok := m.LastFrame().Source.(*SwitchStmt) + // this is handled in the preprocessor + // should never happen if !ok { - // fallthrough is only allowed in a switch statement panic("fallthrough statement out of place") } - if ss.IsTypeSwitch { - // fallthrough is not allowed in type switches - panic("cannot fallthrough in type switch") - } + b := m.LastBlock() - if b.bodyStmt.NextBodyIndex != len(b.bodyStmt.Body) { - // fallthrough is not the final statement - panic("fallthrough statement out of place") - } // compute next switch clause from BodyIndex (assigned in preprocess) nextClause := cs.BodyIndex + 1 - if nextClause >= len(ss.Clauses) { - // no more clause after the one executed, this is not allowed - panic("cannot fallthrough final case in switch") - } // expand block size cl := ss.Clauses[nextClause] if nn := cl.GetNumNames(); int(nn) > len(b.Values) { diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 2d65dfa5cb1..23a02848600 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -298,6 +298,11 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { last.Predefine(false, n.VarName) } case *SwitchClauseStmt: + blen := len(n.Body) + if blen > 0 { + n.Body[blen-1].SetAttribute(ATTR_LAST_BLOCK_STMT, true) + } + // parent switch statement. ss := ns[len(ns)-1].(*SwitchStmt) // anything declared in ss are copied, @@ -2125,8 +2130,19 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *BranchStmt: + + notAllowedFunc := func(s string) { + _, isFunc := last.(*FuncLitExpr) + + if isFunc { + panic(fmt.Sprintf("%s statement out of place", s)) + } + } + switch n.Op { case BREAK: + notAllowedFunc("break") + if n.Label == "" { if !findBreakableNode(ns) { panic("cannot break with no parent loop or switch") @@ -2139,6 +2155,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } } case CONTINUE: + notAllowedFunc("continue") + if n.Label == "" { if !findContinuableNode(ns) { panic("cannot continue with no parent loop") @@ -2154,17 +2172,32 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { n.Depth = depth n.BodyIndex = index case FALLTHROUGH: - if swchC, ok := last.(*SwitchClauseStmt); ok { - // last is a switch clause, find its index in the switch and assign - // it to the fallthrough node BodyIndex. This will be used at - // runtime to determine the next switch clause to run. - swch := lastSwitch(ns) - for i := range swch.Clauses { - if &swch.Clauses[i] == swchC { - // switch clause found - n.BodyIndex = i - break - } + swchC, ok := last.(*SwitchClauseStmt) + if !ok { + // fallthrough is only allowed in a switch statement + panic("fallthrough statement out of place") + } + + if n.GetAttribute(ATTR_LAST_BLOCK_STMT) != true { + // no more clause after the one executed, this is not allowed + panic("cannot fallthrough final case in switch") + } + + // last is a switch clause, find its index in the switch and assign + // it to the fallthrough node BodyIndex. This will be used at + // runtime to determine the next switch clause to run. + swch := lastSwitch(ns) + + if swch.IsTypeSwitch { + // fallthrough is not allowed in type switches + panic("cannot fallthrough in type switch") + } + + for i := range swch.Clauses { + if &swch.Clauses[i] == swchC { + // switch clause found + n.BodyIndex = i + break } } default: diff --git a/gnovm/tests/files/for21.gno b/gnovm/tests/files/for21.gno new file mode 100644 index 00000000000..a41a6cdc0dd --- /dev/null +++ b/gnovm/tests/files/for21.gno @@ -0,0 +1,17 @@ +package main + +func main() { + for i := 0; i < 10; i++ { + if i == 1 { + _ = func() int { + continue + return 11 + }() + } + println(i) + } + println("wat???") +} + +// Error: +// continue statement out of place diff --git a/gnovm/tests/files/for22.gno b/gnovm/tests/files/for22.gno new file mode 100644 index 00000000000..63d29c5a0d5 --- /dev/null +++ b/gnovm/tests/files/for22.gno @@ -0,0 +1,17 @@ +package main + +func main() { + for i := 0; i < 10; i++ { + if i == 1 { + _ = func() int { + fallthrough + return 11 + }() + } + println(i) + } + println("wat???") +} + +// Error: +// fallthrough statement out of place \ No newline at end of file diff --git a/gnovm/tests/files/for23.gno b/gnovm/tests/files/for23.gno new file mode 100644 index 00000000000..0f4738fbea9 --- /dev/null +++ b/gnovm/tests/files/for23.gno @@ -0,0 +1,17 @@ +package main + +func main() { + for i := 0; i < 10; i++ { + if i == 1 { + _ = func() int { + break + return 11 + }() + } + println(i) + } + println("wat???") +} + +// Error: +// break statement out of place \ No newline at end of file From 6034015c07eb62685d7e2767fbfe7f6542713cb2 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Tue, 29 Oct 2024 02:31:03 +0100 Subject: [PATCH 02/11] fix test --- gnovm/tests/files/for21.gno | 2 +- gnovm/tests/files/for22.gno | 2 +- gnovm/tests/files/for23.gno | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gnovm/tests/files/for21.gno b/gnovm/tests/files/for21.gno index a41a6cdc0dd..74b7d724121 100644 --- a/gnovm/tests/files/for21.gno +++ b/gnovm/tests/files/for21.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// continue statement out of place +// main/files/for21.gno:7:17: continue statement out of place diff --git a/gnovm/tests/files/for22.gno b/gnovm/tests/files/for22.gno index 63d29c5a0d5..dd86ce97cb7 100644 --- a/gnovm/tests/files/for22.gno +++ b/gnovm/tests/files/for22.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// fallthrough statement out of place \ No newline at end of file +// main/files/for22.gno:7:17: fallthrough statement out of place \ No newline at end of file diff --git a/gnovm/tests/files/for23.gno b/gnovm/tests/files/for23.gno index 0f4738fbea9..cb0f4c104fa 100644 --- a/gnovm/tests/files/for23.gno +++ b/gnovm/tests/files/for23.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// break statement out of place \ No newline at end of file +// main/files/for23.gno:7:17: break statement out of place \ No newline at end of file From 500197ed9154fa4853f45fb8e58ef1342caae4d4 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Tue, 29 Oct 2024 02:38:11 +0100 Subject: [PATCH 03/11] fix test --- gnovm/tests/files/switch8.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/switch8.gno b/gnovm/tests/files/switch8.gno index c43c72582c0..f5952354270 100644 --- a/gnovm/tests/files/switch8.gno +++ b/gnovm/tests/files/switch8.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// fallthrough statement out of place +// main/files/switch8.gno:5:2: fallthrough statement out of place From 1b7126848848a47e775b10124a48a4d163104e8b Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Tue, 29 Oct 2024 03:07:05 +0100 Subject: [PATCH 04/11] fix tests --- gnovm/pkg/gnolang/preprocess.go | 6 +++++- gnovm/tests/files/switch8b.gno | 2 +- gnovm/tests/files/switch8c.gno | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 23a02848600..d71d1723ac4 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2180,7 +2180,7 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { if n.GetAttribute(ATTR_LAST_BLOCK_STMT) != true { // no more clause after the one executed, this is not allowed - panic("cannot fallthrough final case in switch") + panic("fallthrough statement out of place") } // last is a switch clause, find its index in the switch and assign @@ -2194,6 +2194,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } for i := range swch.Clauses { + if i == len(swch.Clauses)-1 { + panic("cannot fallthrough final case in switch") + } + if &swch.Clauses[i] == swchC { // switch clause found n.BodyIndex = i diff --git a/gnovm/tests/files/switch8b.gno b/gnovm/tests/files/switch8b.gno index cdf35caf784..b25bb68509c 100644 --- a/gnovm/tests/files/switch8b.gno +++ b/gnovm/tests/files/switch8b.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// cannot fallthrough final case in switch +// main/files/switch8b.gno:9:8: cannot fallthrough final case in switch diff --git a/gnovm/tests/files/switch8c.gno b/gnovm/tests/files/switch8c.gno index 6897b8a88fd..cea827d38cd 100644 --- a/gnovm/tests/files/switch8c.gno +++ b/gnovm/tests/files/switch8c.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// fallthrough statement out of place +// main/files/switch8c.gno:6:8: fallthrough statement out of place From 348b2491cb53be16447801b7fa9c8ebd1dcb35b2 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Tue, 29 Oct 2024 03:14:07 +0100 Subject: [PATCH 05/11] fix tests --- gnovm/tests/files/switch8b.gno | 2 +- gnovm/tests/files/switch8c.gno | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gnovm/tests/files/switch8b.gno b/gnovm/tests/files/switch8b.gno index b25bb68509c..079b1b48efe 100644 --- a/gnovm/tests/files/switch8b.gno +++ b/gnovm/tests/files/switch8b.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// main/files/switch8b.gno:9:8: cannot fallthrough final case in switch +// main/files/switch8b.gno:10:3: cannot fallthrough final case in switch diff --git a/gnovm/tests/files/switch8c.gno b/gnovm/tests/files/switch8c.gno index cea827d38cd..27c8e3ad9d5 100644 --- a/gnovm/tests/files/switch8c.gno +++ b/gnovm/tests/files/switch8c.gno @@ -12,4 +12,4 @@ func main() { } // Error: -// main/files/switch8c.gno:6:8: fallthrough statement out of place +// main/files/switch8c.gno:7:3: fallthrough statement out of place From 26862b6da0a30ea2ae38a39f8096ca671929f331 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Tue, 29 Oct 2024 03:43:39 +0100 Subject: [PATCH 06/11] save --- gnovm/tests/files/switch9.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/switch9.gno b/gnovm/tests/files/switch9.gno index 5f596de013a..5b05316b0b9 100644 --- a/gnovm/tests/files/switch9.gno +++ b/gnovm/tests/files/switch9.gno @@ -13,4 +13,4 @@ func main() { } // Error: -// cannot fallthrough in type switch +// main/files/switch9.gno:9:3: cannot fallthrough in type switch From 477e08b11bf20f7280d3eb65ced92353a7036d36 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Sun, 10 Nov 2024 02:21:50 +0100 Subject: [PATCH 07/11] save --- gnovm/pkg/gnolang/preprocess.go | 15 ++++++++++++--- gnovm/tests/files/for24.gno | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 gnovm/tests/files/for24.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 349d4c63474..d0f6800d256 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2141,10 +2141,19 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { case *BranchStmt: notAllowedFunc := func(s string) { - _, isFunc := last.(*FuncLitExpr) + for last != nil { + switch last.(type) { + case *FuncLitExpr: + panic(fmt.Sprintf("%s statement out of place", s)) + case *SwitchStmt: + return + case *SwitchClauseStmt: + return + case *ForStmt: + return + } - if isFunc { - panic(fmt.Sprintf("%s statement out of place", s)) + last = last.GetParentNode(store) } } diff --git a/gnovm/tests/files/for24.gno b/gnovm/tests/files/for24.gno new file mode 100644 index 00000000000..c3d49bb86a7 --- /dev/null +++ b/gnovm/tests/files/for24.gno @@ -0,0 +1,19 @@ +package main + +func main() { + for i := 0; i < 10; i++ { + if i == 1 { + _ = func() int { + if true { + break + } + return 11 + }() + } + println(i) + } + println("wat???") +} + +// Error: +// main/files/for24.gno:8:21: break statement out of place \ No newline at end of file From 4a7f2be4c3d8e823d158b2e93d1366d34b7c12db Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Sun, 10 Nov 2024 02:52:18 +0100 Subject: [PATCH 08/11] save --- gnovm/pkg/gnolang/preprocess.go | 66 +++++++++++++-------------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index d0f6800d256..14bc12a73a7 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2139,32 +2139,10 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *BranchStmt: - - notAllowedFunc := func(s string) { - for last != nil { - switch last.(type) { - case *FuncLitExpr: - panic(fmt.Sprintf("%s statement out of place", s)) - case *SwitchStmt: - return - case *SwitchClauseStmt: - return - case *ForStmt: - return - } - - last = last.GetParentNode(store) - } - } - switch n.Op { case BREAK: - notAllowedFunc("break") - if n.Label == "" { - if !findBreakableNode(ns) { - panic("cannot break with no parent loop or switch") - } + findBreakableNode(last, store) } else { // Make sure that the label exists, either for a switch or a // BranchStmt. @@ -2173,12 +2151,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { } } case CONTINUE: - notAllowedFunc("continue") - if n.Label == "" { - if !findContinuableNode(ns) { - panic("cannot continue with no parent loop") - } + findContinuableNode(last, store) } else { if isSwitchLabel(ns, n.Label) { panic(fmt.Sprintf("invalid continue label %q\n", n.Label)) @@ -3318,24 +3292,36 @@ func funcOf(last BlockNode) (BlockNode, *FuncTypeExpr) { } } -func findBreakableNode(ns []Node) bool { - for _, n := range ns { - switch n.(type) { - case *ForStmt, *RangeStmt, *SwitchClauseStmt: - return true +func findBreakableNode(last BlockNode, store Store) { + for last != nil { + switch last.(type) { + case *FuncLitExpr: + panic("break statement out of place") + case *ForStmt: + return + case *RangeStmt: + return + case *SwitchClauseStmt: + return } + + last = last.GetParentNode(store) } - return false } -func findContinuableNode(ns []Node) bool { - for _, n := range ns { - switch n.(type) { - case *ForStmt, *RangeStmt: - return true +func findContinuableNode(last BlockNode, store Store) { + for last != nil { + switch last.(type) { + case *FuncLitExpr: + panic("continue statement out of place") + case *ForStmt: + return + case *RangeStmt: + return } + + last = last.GetParentNode(store) } - return false } func findBranchLabel(last BlockNode, label Name) ( From 91ea27538b313a35208caa906606dd795e2593ae Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Sun, 10 Nov 2024 03:00:27 +0100 Subject: [PATCH 09/11] save --- gnovm/pkg/gnolang/preprocess.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 14bc12a73a7..1b85d83296d 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -3295,7 +3295,7 @@ func funcOf(last BlockNode) (BlockNode, *FuncTypeExpr) { func findBreakableNode(last BlockNode, store Store) { for last != nil { switch last.(type) { - case *FuncLitExpr: + case *FuncLitExpr, *FuncDecl: panic("break statement out of place") case *ForStmt: return @@ -3312,7 +3312,7 @@ func findBreakableNode(last BlockNode, store Store) { func findContinuableNode(last BlockNode, store Store) { for last != nil { switch last.(type) { - case *FuncLitExpr: + case *FuncLitExpr, *FuncDecl: panic("continue statement out of place") case *ForStmt: return From 591398519b86845e8a2e71133a5186f7f7cb6317 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Sun, 10 Nov 2024 03:22:18 +0100 Subject: [PATCH 10/11] save --- gnovm/tests/files/break0.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/break0.gno b/gnovm/tests/files/break0.gno index 17d68dc1dbf..891084c56f9 100644 --- a/gnovm/tests/files/break0.gno +++ b/gnovm/tests/files/break0.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/break0.gno:4:2: cannot break with no parent loop or switch +// main/files/break0.gno:4:2: break statement out of place From 919f650e1e0b4bcb65c2535a576e7b0238be9068 Mon Sep 17 00:00:00 2001 From: Petar Dambovaliev Date: Sun, 10 Nov 2024 12:42:30 +0100 Subject: [PATCH 11/11] save --- gnovm/tests/files/cont3.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/tests/files/cont3.gno b/gnovm/tests/files/cont3.gno index 8a305d4ceb2..39112697860 100644 --- a/gnovm/tests/files/cont3.gno +++ b/gnovm/tests/files/cont3.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/cont3.gno:4:2: cannot continue with no parent loop +// main/files/cont3.gno:4:2: continue statement out of place