From a0e7830396ffadf07fd0b1c4e475766ed15436cf Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 16:47:26 -0700 Subject: [PATCH 1/8] initial commit --- gnovm/pkg/gnolang/nodes.go | 16 +++ gnovm/pkg/gnolang/preprocess.go | 211 ++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index b127cd32421..fd60a648d8c 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1459,6 +1459,7 @@ type BlockNode interface { GetNumNames() uint16 GetParentNode(Store) BlockNode GetPathForName(Store, Name) ValuePath + GetExternPathForName(Store, Name) ValuePath GetIsConst(Store, Name) bool GetLocalIndex(Name) (uint16, bool) GetValueRef(Store, Name) *TypedValue @@ -1557,6 +1558,7 @@ func (sb *StaticBlock) GetBlockNames() (ns []Name) { } // Implements BlockNode. +// NOTE: Extern names may also be local, if declared later. func (sb *StaticBlock) GetExternNames() (ns []Name) { return sb.Externs // copy? } @@ -1598,6 +1600,8 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath { } // Register as extern. // NOTE: uverse names are externs too. + // NOTE: if a name is later declared in this block later, it is both an + // extern name with depth > 1, as well as local name with depth == 1. if !isFile(sb.GetSource(store)) { sb.GetStaticBlock().addExternName(n) } @@ -1626,6 +1630,18 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath { panic(fmt.Sprintf("name %s not declared", n)) } +// Like GetPathForName, but always returns the path of the extern name. +// This is relevant for when a name is declared later in the block. +func (sb *StaticBlock) GetExternPathForName(store Store, n Name) ValuePath { + if n == "_" { + return NewValuePathBlock(0, 0, "_") + } + parent := n.GetParentNode(store) + path := parent.GetPathForName(store, n) + path.Depth += 1 + return path +} + // Returns whether a name defined here in in ancestry is a const. // This is not the same as whether a name's static type is // untyped -- as in c := a == b, a name may be an untyped non-const. diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index f4a42e1e583..71b522c784a 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -737,6 +737,75 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { cx := evalConst(store, last, n) return cx, TRANS_CONTINUE + // TRANS_LEAVE ----------------------- + case *FuncLitExpr: + // We need to closure-call if any variables + // used were declared in a for-loop, and this + // is the outer-most function within the loop. + // NOTE: if the variable is defined as a + // function parameter of the closure, then it + // is shadowed and not relevant whether or not + // the closure is immediately called. + + // Step 1: check if this func lit is directly in + // a loop, not embedded within another func lit. + // XXX pass store + if !isFuncLitDirectlyInLoop(n) { + return n, TRANS_CONTINUE + } + + // Step 2: check if there are any names + // declared in any containing for-loop (but + // break when encountering another func lit, as + // the later recursion for that func lit will + // handle it there) + leNames := []Name{} + leTypes := []Type{} + lePaths := []ValuePath{} + for name := range n.GetExternNames() { + path := n.GetExternPathForName(store, name) + if path.Type != VPBlock { + continue // not a for-loop block path + } + container := getBlockNodeForPath(n, path) + if _, ok := container.(*ForStmt); !ok { + continue // not a for-loop declared name + } + typ := n.GetStaticTypeOfAt(store, path) + // Append loop extern name and related. + leNames = append(leNames, name) + lePaths = append(lePaths, path) + leTypes = append(leTypes, typ) + } + if len(leNames) == 0 { + // nothing to transform + return n, TRANS_CONTINUE + } + // Finally, do the transform. + // The inner closure loop externs will be + // modified to have a path depth of 2 (or more) + // (1+1 to reach the outer closure ), while all + // other inner non-loop-externs names will have + // path depths +1, while non-extern names will + // not be affected. + // The outer closure call’s passed arguments + // will be modified by the final preprocess + // call on the call expression (variable). + adjustLoopExternPaths(n, loopExterns, loopExternPaths) + // Inject a closure-call and preprocess it. + fldargs := []interface{}{} + for i := 0; i < len(loopExterns); i++ { + fldargs = append(fldargs, + loopExterns[i], + constType(nil, loopExternTypes[i]), + ) + } + params := Flds(fldargs...) + closure := Fn(params, nil, n) + call := Call(closure, loopExterns...) + call = Preprocess(store, last, call).(*CallExpr) + return call, TRANS_CONTINUE + // TRANS_LEAVE ----------------------- case *BinaryExpr: lt := evalStaticTypeOf(store, last, n.Left) @@ -3559,6 +3628,148 @@ func findDependentNames(n Node, dst map[Name]struct{}) { } } +// XXX pass store +func isFuncLitDirectlyInLoop(n Node) bool { + if _, ok := n.(*FuncLitExpr); !ok { + panic("expected func lit, got something else") + } + for n != nil { + pn := n.GetParentNode(nil) + switch cpn := pn.(type) { + case *FuncLitExpr: + return false + case *ForStmt: + return true + default: + continue + } + } +} + +// bn: block node within which path was gotten. +// XXX pass store +// XXX consider as a method for BlockNode. +func getBlockNodeForPath(bn BlockNode, path ValuePath) BlockNode { + if path.Type != VPBlock { + panic("expected block type value path but got something else") + } + + // NOTE: path.Depth == 1 means it's in bn. + for i := 1; i < path.Depth; i++ { + bn = bn.GetParentNode(nil) + } + + return bn +} + +func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { + + var depthOffset = 0 + isLeName := func(name Name) (ValuePath, bool) { + for i, leName := range leNames { + if name == leName { + return lePaths[i], true + } + } + return ValuePath{}, false + } + + nn := Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + // expect only preprocessed nodes. + if n.GetAttribute(ATTR_PREPROCESSED) != true { + panic("expected only preprocessed nodes for adjustLoopExrternPaths") + } + + switch stage { + case TRANS_ENTER: + switch cn := n.(type) { + case NameExpr: + if cn.Path.Type != VPBlockType { + panic("unexpected value path type for name") + } + lePath, isLeName := isLeName(cn.Name) + if isLeName { + // sanity check + if cn.Path.Depth > + lePath.Depth+depthOffset { + panic("should not happen") + } + // If the depth is shallow, + // it does not apply, was redeclared. + if cn.Path.Depth < + lePath.Depth+depthOffset { + return cn, TRANS_CONTINUE + } + // e.g. + // for { + // i := 0 + // for { + // func() { // offset 0 + // func() { // offset 1 + // i // depth 4 + // } + // } + // } + // } + // becomes + // for { + // i := 0 + // for { + // func(i ) { + // func() { // offset 0 + // func() { // offset 1 + // i // depth of 1+1+1 + // // 1 for base inner + // // 1 for offset + // // 1 for outer closure + // } + // } + // }(i) + // } + // } + cn.Path.Depth = 1 + offset + 1 + } else { + // If the depth is shallow, + // it is not affected. + if cn.Path.Depth <= + 1+depthOffset { + return cn, TRANS_CONTINUE + } else { + // Non-loop extern names are + // bumped by 1. + cn.Path.Depth += 1 + } + } + return cn, TRANS_CONTINUE + /* + case *FuncLitExpr: + // if func lit already has all args + // declared, we could skip recursion + // here, but tracking that would be + // cumbersome so just ignore and + // continue. + continue // "continue" to track offset. + */ + case BlockNode: + depthOffset += 1 // keep track of depth + return cn, TRANS_CONTINUE + default: + return cn, TRANS_SKIP + } + case TRANS_LEAVE: + switch cn := n.(type) { + case BlockNode: + depthOffset -= 1 // keep track of depth + return cn, TRANS_CONTINUE + default: + return cn, TRANS_SKIP + } + default: + return cn, TRANS_CONTINUE + } + }) +} + // ---------------------------------------- // SetNodeLocations From fe362a1d9a61564ea678aa8b3f0b47097cc986cc Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 17:03:00 -0700 Subject: [PATCH 2/8] fix name and comments for isFuncLitInLoop --- gnovm/pkg/gnolang/preprocess.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 71b522c784a..ead4b8e29b3 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -747,10 +747,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // is shadowed and not relevant whether or not // the closure is immediately called. - // Step 1: check if this func lit is directly in - // a loop, not embedded within another func lit. - // XXX pass store - if !isFuncLitDirectlyInLoop(n) { + // Step 1: check if this func lit is in a loop, + // not embedded within another func lit. + if !isFuncLitInLoop(store, n) { return n, TRANS_CONTINUE } @@ -790,7 +789,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // not be affected. // The outer closure call’s passed arguments // will be modified by the final preprocess - // call on the call expression (variable). + // call on the call expression (variable depth). adjustLoopExternPaths(n, loopExterns, loopExternPaths) // Inject a closure-call and preprocess it. fldargs := []interface{}{} @@ -803,6 +802,9 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { params := Flds(fldargs...) closure := Fn(params, nil, n) call := Call(closure, loopExterns...) + // this preprocess will also set the path for the + // outer closure's params and the call's + // argument value paths. call = Preprocess(store, last, call).(*CallExpr) return call, TRANS_CONTINUE @@ -3628,14 +3630,15 @@ func findDependentNames(n Node, dst map[Name]struct{}) { } } -// XXX pass store -func isFuncLitDirectlyInLoop(n Node) bool { +// return true if node is in a loop, +// but not embedded within another func lit intermediary. +func isFuncLitInLoop(store Store, n Node) bool { if _, ok := n.(*FuncLitExpr); !ok { panic("expected func lit, got something else") } for n != nil { - pn := n.GetParentNode(nil) - switch cpn := pn.(type) { + n = n.GetParentNode(store) + switch cn := n.(type) { case *FuncLitExpr: return false case *ForStmt: From 2eab0d396c9bbd81a5b249048b2e322bcb0ef692 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 17:06:43 -0700 Subject: [PATCH 3/8] ... --- gnovm/pkg/gnolang/preprocess.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index ead4b8e29b3..7a7e7d75291 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -753,11 +753,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { return n, TRANS_CONTINUE } - // Step 2: check if there are any names - // declared in any containing for-loop (but - // break when encountering another func lit, as - // the later recursion for that func lit will - // handle it there) + // Step 2: gather all extern names declared in for-loops. leNames := []Name{} leTypes := []Type{} lePaths := []ValuePath{} @@ -766,6 +762,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if path.Type != VPBlock { continue // not a for-loop block path } + // XXX this is wrong, need to check all ancestors + // rather than the block node for path. container := getBlockNodeForPath(n, path) if _, ok := container.(*ForStmt); !ok { continue // not a for-loop declared name @@ -3632,10 +3630,8 @@ func findDependentNames(n Node, dst map[Name]struct{}) { // return true if node is in a loop, // but not embedded within another func lit intermediary. -func isFuncLitInLoop(store Store, n Node) bool { - if _, ok := n.(*FuncLitExpr); !ok { - panic("expected func lit, got something else") - } +func isFuncLitInLoop(store Store, fn *FuncLitExpr) bool { + var n BlockNode = fn for n != nil { n = n.GetParentNode(store) switch cn := n.(type) { From 3c5603cc35c6d044c7c350ea6540a25fd50ddb55 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 17:51:36 -0700 Subject: [PATCH 4/8] reuse isNodeInLoop --- gnovm/pkg/gnolang/preprocess.go | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 7a7e7d75291..97f81390135 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -749,7 +749,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Step 1: check if this func lit is in a loop, // not embedded within another func lit. - if !isFuncLitInLoop(store, n) { + if !isNodeInLoop(store, n) { return n, TRANS_CONTINUE } @@ -762,10 +762,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if path.Type != VPBlock { continue // not a for-loop block path } - // XXX this is wrong, need to check all ancestors - // rather than the block node for path. - container := getBlockNodeForPath(n, path) - if _, ok := container.(*ForStmt); !ok { + if !isNodeInLoop(store, n) { continue // not a for-loop declared name } typ := n.GetStaticTypeOfAt(store, path) @@ -3630,8 +3627,7 @@ func findDependentNames(n Node, dst map[Name]struct{}) { // return true if node is in a loop, // but not embedded within another func lit intermediary. -func isFuncLitInLoop(store Store, fn *FuncLitExpr) bool { - var n BlockNode = fn +func isNodeInLoop(store Store, n Node) bool { for n != nil { n = n.GetParentNode(store) switch cn := n.(type) { @@ -3645,24 +3641,7 @@ func isFuncLitInLoop(store Store, fn *FuncLitExpr) bool { } } -// bn: block node within which path was gotten. -// XXX pass store -// XXX consider as a method for BlockNode. -func getBlockNodeForPath(bn BlockNode, path ValuePath) BlockNode { - if path.Type != VPBlock { - panic("expected block type value path but got something else") - } - - // NOTE: path.Depth == 1 means it's in bn. - for i := 1; i < path.Depth; i++ { - bn = bn.GetParentNode(nil) - } - - return bn -} - func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { - var depthOffset = 0 isLeName := func(name Name) (ValuePath, bool) { for i, leName := range leNames { From ccb5f27f359b3dfd37fd7e509df8b12c0d9ab010 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 17:54:51 -0700 Subject: [PATCH 5/8] ... --- gnovm/pkg/gnolang/nodes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index fd60a648d8c..e01f43ca6ce 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1636,7 +1636,7 @@ func (sb *StaticBlock) GetExternPathForName(store Store, n Name) ValuePath { if n == "_" { return NewValuePathBlock(0, 0, "_") } - parent := n.GetParentNode(store) + parent := sb.GetParentNode(store) path := parent.GetPathForName(store, n) path.Depth += 1 return path From 107252dfab8c6d8d0a973704d15ee98c12574beb Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 18:44:58 -0700 Subject: [PATCH 6/8] also check externs against outer loops --- gnovm/pkg/gnolang/nodes.go | 16 ++++++++++++++++ gnovm/pkg/gnolang/preprocess.go | 25 ++++++++++++++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index e01f43ca6ce..72af3ee8464 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1460,6 +1460,7 @@ type BlockNode interface { GetParentNode(Store) BlockNode GetPathForName(Store, Name) ValuePath GetExternPathForName(Store, Name) ValuePath + GetBlockNodeForPath(ValuePath) BlockNode GetIsConst(Store, Name) bool GetLocalIndex(Name) (uint16, bool) GetValueRef(Store, Name) *TypedValue @@ -1642,6 +1643,21 @@ func (sb *StaticBlock) GetExternPathForName(store Store, n Name) ValuePath { return path } +// Get the containing block node for node with path relative to this containing block. +func (sb *StaticBlock) GetBlockNodeForPath(store Store, path ValuePath) BlockNode { + if path.Type != VPBlock { + panic("expected block type value path but got something else") + } + + // NOTE: path.Depth == 1 means it's in bn. + var bn BlockNode = sb.GetSource(store) + for i := 1; i < path.Depth; i++ { + bn = bn.GetParentNode(store) + } + + return bn +} + // Returns whether a name defined here in in ancestry is a const. // This is not the same as whether a name's static type is // untyped -- as in c := a == b, a name may be an untyped non-const. diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 97f81390135..e161946ce2a 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -749,7 +749,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Step 1: check if this func lit is in a loop, // not embedded within another func lit. - if !isNodeInLoop(store, n) { + loopDepth, inLoop := isNodeInLoop(store, n) + if !inLoop { return n, TRANS_CONTINUE } @@ -762,8 +763,15 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if path.Type != VPBlock { continue // not a for-loop block path } - if !isNodeInLoop(store, n) { - continue // not a for-loop declared name + if path.Depth <= loopDepth+1 { + // it is within the loop, carry on... + } else { + // it is outside the closest loop ancestor, + // check to see if it is declared in outer loop. + container := n.GetBlockNodeForPath(store, path) + if _, ok := isNodeInLoop(container); !ok { + continue // not declared in any loop. + } } typ := n.GetStaticTypeOfAt(store, path) // Append loop extern name and related. @@ -3625,20 +3633,23 @@ func findDependentNames(n Node, dst map[Name]struct{}) { } } -// return true if node is in a loop, +// return the depth offset of loop and true if node is in a loop, // but not embedded within another func lit intermediary. -func isNodeInLoop(store Store, n Node) bool { +func isNodeInLoop(store Store, n Node) (int, bool) { + depthOffset := 0 for n != nil { + depthOffset += 1 n = n.GetParentNode(store) switch cn := n.(type) { case *FuncLitExpr: - return false + return 0, false case *ForStmt: - return true + return depthOffset, true default: continue } } + return 0, false } func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { From d28f7b6e6c1a8805795d9cf5b584af93b369391b Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 19:23:47 -0700 Subject: [PATCH 7/8] fix build errors --- gnovm/pkg/gnolang/nodes.go | 4 +-- gnovm/pkg/gnolang/preprocess.go | 57 ++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 72af3ee8464..b5ae6e96662 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -1460,7 +1460,7 @@ type BlockNode interface { GetParentNode(Store) BlockNode GetPathForName(Store, Name) ValuePath GetExternPathForName(Store, Name) ValuePath - GetBlockNodeForPath(ValuePath) BlockNode + GetBlockNodeForPath(Store, ValuePath) BlockNode GetIsConst(Store, Name) bool GetLocalIndex(Name) (uint16, bool) GetValueRef(Store, Name) *TypedValue @@ -1651,7 +1651,7 @@ func (sb *StaticBlock) GetBlockNodeForPath(store Store, path ValuePath) BlockNod // NOTE: path.Depth == 1 means it's in bn. var bn BlockNode = sb.GetSource(store) - for i := 1; i < path.Depth; i++ { + for i := 1; i < int(path.Depth); i++ { bn = bn.GetParentNode(store) } diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index e161946ce2a..79a2bffa976 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -749,7 +749,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // Step 1: check if this func lit is in a loop, // not embedded within another func lit. - loopDepth, inLoop := isNodeInLoop(store, n) + loopDepth, inLoop := isBlockNodeInLoop(store, n) if !inLoop { return n, TRANS_CONTINUE } @@ -758,18 +758,18 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { leNames := []Name{} leTypes := []Type{} lePaths := []ValuePath{} - for name := range n.GetExternNames() { + for _, name := range n.GetExternNames() { path := n.GetExternPathForName(store, name) if path.Type != VPBlock { continue // not a for-loop block path } - if path.Depth <= loopDepth+1 { + if int(path.Depth) <= loopDepth+1 { // it is within the loop, carry on... } else { // it is outside the closest loop ancestor, // check to see if it is declared in outer loop. container := n.GetBlockNodeForPath(store, path) - if _, ok := isNodeInLoop(container); !ok { + if _, ok := isBlockNodeInLoop(store, container); !ok { continue // not declared in any loop. } } @@ -793,18 +793,25 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // The outer closure call’s passed arguments // will be modified by the final preprocess // call on the call expression (variable depth). - adjustLoopExternPaths(n, loopExterns, loopExternPaths) + adjustLoopExternPaths(n, leNames, lePaths) // Inject a closure-call and preprocess it. - fldargs := []interface{}{} - for i := 0; i < len(loopExterns); i++ { - fldargs = append(fldargs, - loopExterns[i], - constType(nil, loopExternTypes[i]), + paramFields := []interface{}{} + for i := 0; i < len(leNames); i++ { + paramFields = append(paramFields, + leNames[i], + constType(nil, leTypes[i]), ) } - params := Flds(fldargs...) - closure := Fn(params, nil, n) - call := Call(closure, loopExterns...) + params := Flds(paramFields...) + fnType := evalStaticTypeOf(store, last, n) + results := Flds("", constType(nil, fnType)) + returnFn := Return(n) + closure := Fn(params, results, []Stmt{returnFn}) + leNameExprs := []interface{}{} + for _, leName := range leNames { + leNameExprs = append(leNameExprs, Nx(leName)) + } + call := Call(closure, leNameExprs...) // this preprocess will also set the path for the // outer closure's params and the call's // argument value paths. @@ -3633,14 +3640,14 @@ func findDependentNames(n Node, dst map[Name]struct{}) { } } -// return the depth offset of loop and true if node is in a loop, +// return the depth offset of loop and true if block node is in a loop, // but not embedded within another func lit intermediary. -func isNodeInLoop(store Store, n Node) (int, bool) { +func isBlockNodeInLoop(store Store, bn BlockNode) (int, bool) { depthOffset := 0 - for n != nil { + for bn != nil { depthOffset += 1 - n = n.GetParentNode(store) - switch cn := n.(type) { + bn = bn.GetParentNode(store) + switch bn.(type) { case *FuncLitExpr: return 0, false case *ForStmt: @@ -3652,8 +3659,8 @@ func isNodeInLoop(store Store, n Node) (int, bool) { return 0, false } -func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { - var depthOffset = 0 +func adjustLoopExternPaths(bn BlockNode, leNames []Name, lePaths []ValuePath) { + var depthOffset uint8 = 0 isLeName := func(name Name) (ValuePath, bool) { for i, leName := range leNames { if name == leName { @@ -3663,7 +3670,7 @@ func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { return ValuePath{}, false } - nn := Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { // expect only preprocessed nodes. if n.GetAttribute(ATTR_PREPROCESSED) != true { panic("expected only preprocessed nodes for adjustLoopExrternPaths") @@ -3672,8 +3679,8 @@ func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { switch stage { case TRANS_ENTER: switch cn := n.(type) { - case NameExpr: - if cn.Path.Type != VPBlockType { + case *NameExpr: + if cn.Path.Type != VPBlock { panic("unexpected value path type for name") } lePath, isLeName := isLeName(cn.Name) @@ -3716,7 +3723,7 @@ func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { // }(i) // } // } - cn.Path.Depth = 1 + offset + 1 + cn.Path.Depth = 1 + depthOffset + 1 } else { // If the depth is shallow, // it is not affected. @@ -3754,7 +3761,7 @@ func adjustLoopExterns(bn BlockNode, leNames []Name, lePaths []ValuePath) { return cn, TRANS_SKIP } default: - return cn, TRANS_CONTINUE + return n, TRANS_CONTINUE } }) } From fba27463fdf6d510bfbc5f97055937bf3379e1f1 Mon Sep 17 00:00:00 2001 From: jaekwon Date: Fri, 15 Mar 2024 21:43:25 -0700 Subject: [PATCH 8/8] move pop block for block nodes upon leave to defer stmt --- gnovm/pkg/gnolang/preprocess.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 79a2bffa976..6155190fc89 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -617,6 +617,16 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // elide composite lit element (nested) composite types. elideCompositeElements(clx, clt) } + switch n.(type) { + // TRANS_LEAVE (deferred)--------- + // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK + // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR + // POP BLOCK YOURSELF. + case BlockNode: + // Pop block. + stack = stack[:len(stack)-1] + last = stack[len(stack)-1] + } }() // The main TRANS_LEAVE switch. @@ -783,6 +793,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // nothing to transform return n, TRANS_CONTINUE } + panic("!!! ITS HAPPENING (insert ron paul jazz hands) !!!") // Finally, do the transform. // The inner closure loop externs will be // modified to have a path depth of 2 (or more) @@ -2032,17 +2043,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { n.Type = constType(n.Type, dst) } // end type switch statement + // END TRANS_LEAVE ----------------------- - // TRANS_LEAVE ----------------------- - // finalization. - if _, ok := n.(BlockNode); ok { - // Pop block. - stack = stack[:len(stack)-1] - last = stack[len(stack)-1] - return n, TRANS_CONTINUE - } else { - return n, TRANS_CONTINUE - } + // Convenience return in case not already returned. + return n, TRANS_CONTINUE } panic(fmt.Sprintf(