diff --git a/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar b/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar index 0954d1dd932..6099788a9a1 100644 --- a/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/test_with-native-fallback.txtar @@ -4,7 +4,7 @@ ! stdout .+ stderr 'panic: unknown import path net \[recovered\]' -stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path net' +stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path net' gno test -v --with-native-fallback . diff --git a/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar b/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar index 15125f695f5..37ef68f3d91 100644 --- a/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/unknow_lib.txtar @@ -4,13 +4,13 @@ ! stdout .+ stderr 'panic: unknown import path foobarbaz \[recovered\]' -stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path foobarbaz' +stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz' ! gno test -v --with-native-fallback . ! stdout .+ stderr 'panic: unknown import path foobarbaz \[recovered\]' -stderr ' panic: gno.land/r/\w{8}/contract.gno:3:1: unknown import path foobarbaz' +stderr ' panic: gno.land/r/\w{8}/contract.gno:3:8: unknown import path foobarbaz' -- contract.gno -- package contract diff --git a/gnovm/pkg/gnolang/debugger.go b/gnovm/pkg/gnolang/debugger.go index 839b6a691de..f047a176af7 100644 --- a/gnovm/pkg/gnolang/debugger.go +++ b/gnovm/pkg/gnolang/debugger.go @@ -257,8 +257,10 @@ func debugUpdateLocation(m *Machine) { for i := nx - 1; i >= 0; i-- { expr := m.Exprs[i] if l := expr.GetLine(); l > 0 { - m.Debugger.loc.Line = l - m.Debugger.loc.Column = expr.GetColumn() + if col := expr.GetColumn(); col > 0 { + m.Debugger.loc.Line = l + m.Debugger.loc.Column = expr.GetColumn() + } return } } @@ -266,8 +268,10 @@ func debugUpdateLocation(m *Machine) { if len(m.Stmts) > 0 { if stmt := m.PeekStmt1(); stmt != nil { if l := stmt.GetLine(); l > 0 { - m.Debugger.loc.Line = l - m.Debugger.loc.Column = stmt.GetColumn() + if col := stmt.GetColumn(); col > 0 { + m.Debugger.loc.Line = l + m.Debugger.loc.Column = stmt.GetColumn() + } return } } @@ -648,7 +652,7 @@ func debugEvalExpr(m *Machine, node ast.Node) (tv TypedValue, err error) { return tv, fmt.Errorf("invalid selector: %s", n.Sel.Name) } for _, vp := range tr { - x = x.GetPointerTo(m.Alloc, m.Store, vp).Deref() + x = x.GetPointerToFromTV(m.Alloc, m.Store, vp).Deref() } return x, nil case *ast.IndexExpr: diff --git a/gnovm/pkg/gnolang/debugger_test.go b/gnovm/pkg/gnolang/debugger_test.go index fe059ba9f56..44786257d67 100644 --- a/gnovm/pkg/gnolang/debugger_test.go +++ b/gnovm/pkg/gnolang/debugger_test.go @@ -131,7 +131,7 @@ func TestDebug(t *testing.T) { {in: "p \"xxxx\"\n", out: `("xxxx" string)`}, {in: "si\n", out: "sample.gno:14"}, {in: "s\ns\n", out: `=> 14: var global = "test"`}, - {in: "s\n\n", out: "=> 33: num := 5"}, + {in: "s\n\n\n", out: "=> 33: num := 5"}, {in: "foo", out: "command not available: foo"}, {in: "\n\n", out: "dbg> "}, {in: "#\n", out: "dbg> "}, diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go index efdfecf0289..6bde6fb5271 100644 --- a/gnovm/pkg/gnolang/go2gno.go +++ b/gnovm/pkg/gnolang/go2gno.go @@ -754,11 +754,13 @@ func toDecls(fs *token.FileSet, gd *ast.GenDecl) (ds Decls) { name := toName(s.Name) tipe := toExpr(fs, s.Type) alias := s.Assign != 0 - ds = append(ds, &TypeDecl{ + td := &TypeDecl{ NameExpr: NameExpr{Name: name}, Type: tipe, IsAlias: alias, - }) + } + setLoc(fs, s.Pos(), td) + ds = append(ds, td) case *ast.ValueSpec: if gd.Tok == token.CONST { var names []NameExpr diff --git a/gnovm/pkg/gnolang/kind_string.go b/gnovm/pkg/gnolang/kind_string.go index cbe6bfa8e33..12e95829b20 100644 --- a/gnovm/pkg/gnolang/kind_string.go +++ b/gnovm/pkg/gnolang/kind_string.go @@ -36,13 +36,14 @@ func _() { _ = x[MapKind-25] _ = x[TypeKind-26] _ = x[BlockKind-27] - _ = x[TupleKind-28] - _ = x[RefTypeKind-29] + _ = x[HeapItemKind-28] + _ = x[TupleKind-29] + _ = x[RefTypeKind-30] } -const _Kind_name = "InvalidKindBoolKindStringKindIntKindInt8KindInt16KindInt32KindInt64KindUintKindUint8KindUint16KindUint32KindUint64KindFloat32KindFloat64KindBigintKindBigdecKindArrayKindSliceKindPointerKindStructKindPackageKindInterfaceKindChanKindFuncKindMapKindTypeKindBlockKindTupleKindRefTypeKind" +const _Kind_name = "InvalidKindBoolKindStringKindIntKindInt8KindInt16KindInt32KindInt64KindUintKindUint8KindUint16KindUint32KindUint64KindFloat32KindFloat64KindBigintKindBigdecKindArrayKindSliceKindPointerKindStructKindPackageKindInterfaceKindChanKindFuncKindMapKindTypeKindBlockKindHeapItemKindTupleKindRefTypeKind" -var _Kind_index = [...]uint16{0, 11, 19, 29, 36, 44, 53, 62, 71, 79, 88, 98, 108, 118, 129, 140, 150, 160, 169, 178, 189, 199, 210, 223, 231, 239, 246, 254, 263, 272, 283} +var _Kind_index = [...]uint16{0, 11, 19, 29, 36, 44, 53, 62, 71, 79, 88, 98, 108, 118, 129, 140, 150, 160, 169, 178, 189, 199, 210, 223, 231, 239, 246, 254, 263, 275, 284, 295} func (i Kind) String() string { if i >= Kind(len(_Kind_index)-1) { diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index ad94f1a2b3a..1e594de945b 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -699,7 +699,7 @@ func (m *Machine) runFileDecls(fns ...*FileNode) []TypedValue { } } // if dep already in loopfindr, abort. - if hasName(dep, loopfindr) { + if slices.Contains(loopfindr, dep) { if _, ok := (*depdecl).(*FuncDecl); ok { // recursive function dependencies // are OK with func decls. @@ -2112,15 +2112,25 @@ func (m *Machine) PushForPointer(lx Expr) { func (m *Machine) PopAsPointer(lx Expr) PointerValue { switch lx := lx.(type) { case *NameExpr: - lb := m.LastBlock() - return lb.GetPointerTo(m.Store, lx.Path) + switch lx.Type { + case NameExprTypeNormal: + lb := m.LastBlock() + return lb.GetPointerTo(m.Store, lx.Path) + case NameExprTypeHeapUse: + lb := m.LastBlock() + return lb.GetPointerToHeapUse(m.Store, lx.Path) + case NameExprTypeHeapClosure: + panic("should not happen") + default: + panic("unexpected NameExpr in PopAsPointer") + } case *IndexExpr: iv := m.PopValue() xv := m.PopValue() return xv.GetPointerAtIndex(m.Alloc, m.Store, iv) case *SelectorExpr: xv := m.PopValue() - return xv.GetPointerTo(m.Alloc, m.Store, lx.Path) + return xv.GetPointerToFromTV(m.Alloc, m.Store, lx.Path) case *StarExpr: ptr := m.PopValue().V.(PointerValue) return ptr @@ -2348,15 +2358,3 @@ func (m *Machine) ExceptionsStacktrace() string { return builder.String() } - -//---------------------------------------- -// utility - -func hasName(n Name, ns []Name) bool { - for _, n2 := range ns { - if n == n2 { - return true - } - } - return false -} diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index f1bd78ee646..9e7cea8fb7f 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -146,11 +146,26 @@ func (loc Location) IsZero() bool { // even after preprocessing. Temporary attributes (e.g. those // for preprocessing) are stored in .data. +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" +) + type Attributes struct { Line int Column int Label Name - data map[interface{}]interface{} // not persisted + data map[GnoAttribute]interface{} // not persisted } func (attr *Attributes) GetLine() int { @@ -177,22 +192,31 @@ func (attr *Attributes) SetLabel(label Name) { attr.Label = label } -func (attr *Attributes) HasAttribute(key interface{}) bool { +func (attr *Attributes) HasAttribute(key GnoAttribute) bool { _, ok := attr.data[key] return ok } -func (attr *Attributes) GetAttribute(key interface{}) interface{} { +// GnoAttribute must not be user provided / arbitrary, +// otherwise will create potential exploits. +func (attr *Attributes) GetAttribute(key GnoAttribute) interface{} { return attr.data[key] } -func (attr *Attributes) SetAttribute(key interface{}, value interface{}) { +func (attr *Attributes) SetAttribute(key GnoAttribute, value interface{}) { if attr.data == nil { - attr.data = make(map[interface{}]interface{}) + attr.data = make(map[GnoAttribute]interface{}) } attr.data[key] = value } +func (attr *Attributes) DelAttribute(key GnoAttribute) { + if debug && attr.data == nil { + panic("should not happen, attribute is expected to be non-empty.") + } + delete(attr.data, key) +} + // ---------------------------------------- // Node @@ -206,9 +230,10 @@ type Node interface { SetColumn(int) GetLabel() Name SetLabel(Name) - HasAttribute(key interface{}) bool - GetAttribute(key interface{}) interface{} - SetAttribute(key interface{}, value interface{}) + HasAttribute(key GnoAttribute) bool + GetAttribute(key GnoAttribute) interface{} + SetAttribute(key GnoAttribute, value interface{}) + DelAttribute(key GnoAttribute) } // non-pointer receiver to help make immutable. @@ -368,11 +393,22 @@ var ( _ Expr = &ConstExpr{} ) +type NameExprType int + +const ( + NameExprTypeNormal NameExprType = iota // default + NameExprTypeDefine // when defining normally + NameExprTypeHeapDefine // when defining escaped name in loop + NameExprTypeHeapUse // when above used in non-define lhs/rhs + NameExprTypeHeapClosure // when closure captures name +) + type NameExpr struct { Attributes // TODO rename .Path's to .ValuePaths. Path ValuePath // set by preprocessor. Name + Type NameExprType } type NameExprs []NameExpr @@ -499,8 +535,9 @@ type KeyValueExprs []KeyValueExpr type FuncLitExpr struct { Attributes StaticBlock - Type FuncTypeExpr // function type - Body // function body + Type FuncTypeExpr // function type + Body // function body + HeapCaptures NameExprs // filled in findLoopUses1 } // The preprocessor replaces const expressions @@ -581,11 +618,15 @@ func (ftxz FieldTypeExprs) IsNamed() bool { named := false for i, ftx := range ftxz { if i == 0 { - named = ftx.Name != "" + if ftx.Name == "" || isHiddenResultVariable(string(ftx.Name)) { + named = false + } else { + named = true + } } else { if named && ftx.Name == "" { panic("[]FieldTypeExpr has inconsistent namedness (starts named)") - } else if !named && ftx.Name != "" { + } else if !named && (ftx.Name != "" || !isHiddenResultVariable(string(ftx.Name))) { panic("[]FieldTypeExpr has inconsistent namedness (starts unnamed)") } } @@ -1489,6 +1530,7 @@ type BlockNode interface { GetNumNames() uint16 GetParentNode(Store) BlockNode GetPathForName(Store, Name) ValuePath + GetBlockNodeForPath(Store, ValuePath) BlockNode GetIsConst(Store, Name) bool GetLocalIndex(Name) (uint16, bool) GetValueRef(Store, Name, bool) *TypedValue @@ -1588,6 +1630,8 @@ func (sb *StaticBlock) GetBlockNames() (ns []Name) { } // Implements BlockNode. +// NOTE: Extern names may also be local, if declared after usage as an extern +// (thus shadowing the extern name). func (sb *StaticBlock) GetExternNames() (ns []Name) { return sb.Externs // copy? } @@ -1629,6 +1673,9 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath { } // Register as extern. // NOTE: uverse names are externs too. + // NOTE: externs may also be shadowed later in the block. Thus, usages + // before the declaration will have depth > 1; following it, depth == 1, + // matching the two different identifiers they refer to. if !isFile(sb.GetSource(store)) { sb.GetStaticBlock().addExternName(n) } @@ -1657,6 +1704,21 @@ func (sb *StaticBlock) GetPathForName(store Store, n Name) ValuePath { panic(fmt.Sprintf("name %s not declared", n)) } +// 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 " + path.Type.String()) + } + + // NOTE: path.Depth == 1 means it's in bn. + bn := sb.GetSource(store) + for i := 1; i < int(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. @@ -1713,21 +1775,12 @@ func (sb *StaticBlock) GetStaticTypeOf(store Store, n Name) Type { // Implements BlockNode. func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type { if debug { - if path.Type != VPBlock { - panic("should not happen") - } if path.Depth == 0 { panic("should not happen") } } - for { - if path.Depth == 1 { - return sb.Types[path.Index] - } else { - sb = sb.GetParentNode(store).GetStaticBlock() - path.Depth -= 1 - } - } + bn := sb.GetBlockNodeForPath(store, path) + return bn.GetStaticBlock().Types[path.Index] } // Implements BlockNode. @@ -2115,18 +2168,6 @@ func (x *BasicLitExpr) GetInt() int { return i } -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_LOCATIONED" - ATTR_SHIFT_RHS GnoAttribute = "ATTR_SHIFT_RHS" -) - var rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]+$`) // TODO: consider length restrictions. @@ -2136,3 +2177,12 @@ func validatePkgName(name string) { panic(fmt.Sprintf("cannot create package with invalid name %q", name)) } } + +const hiddenResultVariable = ".res_" + +func isHiddenResultVariable(name string) bool { + if strings.HasPrefix(name, hiddenResultVariable) { + return true + } + return false +} diff --git a/gnovm/pkg/gnolang/nodes_string.go b/gnovm/pkg/gnolang/nodes_string.go index 547ad83294d..e16e2f182a5 100644 --- a/gnovm/pkg/gnolang/nodes_string.go +++ b/gnovm/pkg/gnolang/nodes_string.go @@ -96,7 +96,20 @@ func (vp ValuePath) String() string { } func (x NameExpr) String() string { - return fmt.Sprintf("%s<%s>", x.Name, x.Path.String()) + switch x.Type { + case NameExprTypeNormal: + return fmt.Sprintf("%s<%s>", x.Name, x.Path.String()) + case NameExprTypeDefine: + return fmt.Sprintf("%s", x.Name, x.Path.String()) + case NameExprTypeHeapDefine: + return fmt.Sprintf("%s", x.Name, x.Path.String()) + case NameExprTypeHeapUse: + return fmt.Sprintf("%s<~%s>", x.Name, x.Path.String()) + case NameExprTypeHeapClosure: + return fmt.Sprintf("%s<()~%s>", x.Name, x.Path.String()) + default: + panic("unexpected NameExpr type") + } } func (x BasicLitExpr) String() string { @@ -172,7 +185,11 @@ func (x CompositeLitExpr) String() string { } func (x FuncLitExpr) String() string { - return fmt.Sprintf("func %s{ %s }", x.Type, x.Body.String()) + heapCaptures := "" + if len(x.HeapCaptures) > 0 { + heapCaptures = "<" + x.HeapCaptures.String() + ">" + } + return fmt.Sprintf("func %s{ %s }%s", x.Type, x.Body.String(), heapCaptures) } func (x KeyValueExpr) String() string { diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index eb67ffcc351..8caacbfd1e6 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -11,7 +11,7 @@ func (m *Machine) doOpDefine() { // Get name and value of i'th term. nx := s.Lhs[i].(*NameExpr) // Finally, define (or assign if loop block). - ptr := lb.GetPointerTo(m.Store, nx.Path) + ptr := lb.GetPointerToMaybeHeapDefine(m.Store, nx) // XXX HACK (until value persistence impl'd) if m.ReadOnly { if oo, ok := ptr.Base.(Object); ok { diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index 510c308a86a..ba5b7507cff 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -62,6 +62,18 @@ func (m *Machine) doOpCall() { // Create new block scope. clo := fr.Func.GetClosure(m.Store) b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) + + // Copy *FuncValue.Captures into block + // NOTE: addHeapCapture in preprocess ensures order. + if len(fv.Captures) != 0 { + if len(fv.Captures) > len(b.Values) { + panic("should not happen, length of captured variables must not exceed the number of values") + } + for i := 0; i < len(fv.Captures); i++ { + b.Values[len(b.Values)-len(fv.Captures)+i] = fv.Captures[i].Copy(m.Alloc) + } + } + m.PushBlock(b) if fv.nativeBody == nil && fv.NativePkg != "" { // native function, unmarshaled so doesn't have nativeBody yet @@ -83,6 +95,7 @@ func (m *Machine) doOpCall() { // Initialize return variables with default value. numParams := len(ft.Params) for i, rt := range ft.Results { + // results/parameters never are heap use/closure. ptr := b.GetPointerToInt(nil, numParams+i) dtv := defaultTypedValue(m.Alloc, rt.Type) ptr.Assign2(m.Alloc, nil, nil, dtv, false) @@ -292,6 +305,15 @@ func (m *Machine) doOpReturnCallDefers() { // Create new block scope for defer. clo := dfr.Func.GetClosure(m.Store) b := m.Alloc.NewBlock(fv.GetSource(m.Store), clo) + // copy values from captures + if len(fv.Captures) != 0 { + if len(fv.Captures) > len(b.Values) { + panic("should not happen, length of captured variables must not exceed the number of values") + } + for i := 0; i < len(fv.Captures); i++ { + b.Values[len(b.Values)-len(fv.Captures)+i] = fv.Captures[i].Copy(m.Alloc) + } + } m.PushBlock(b) if fv.nativeBody == nil { fbody := fv.GetBodyFromSource(m.Store) diff --git a/gnovm/pkg/gnolang/op_decl.go b/gnovm/pkg/gnolang/op_decl.go index 2c20c43ae2f..c9c04ccf76d 100644 --- a/gnovm/pkg/gnolang/op_decl.go +++ b/gnovm/pkg/gnolang/op_decl.go @@ -58,8 +58,8 @@ func (m *Machine) doOpValueDecl() { } else if isUntyped(tv.T) { ConvertUntypedTo(&tv, nil) } - nx := s.NameExprs[i] - ptr := lb.GetPointerTo(m.Store, nx.Path) + nx := &s.NameExprs[i] + ptr := lb.GetPointerToMaybeHeapDefine(m.Store, nx) ptr.Assign2(m.Alloc, m.Store, m.Realm, tv, false) } } diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index 701615fff13..1beba1d6e3f 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -36,7 +36,7 @@ func (m *Machine) doOpEval() { // Get value from scope. lb := m.LastBlock() // Push value, done. - ptr := lb.GetPointerTo(m.Store, nx.Path) + ptr := lb.GetPointerToMaybeHeapUse(m.Store, nx) m.PushValue(ptr.Deref()) return } diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index c7e8ffd600c..a61349b0806 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -171,8 +171,8 @@ func (m *Machine) doOpExec(op Op) { case ASSIGN: m.PopAsPointer(bs.Key).Assign2(m.Alloc, m.Store, m.Realm, iv, false) case DEFINE: - knxp := bs.Key.(*NameExpr).Path - ptr := m.LastBlock().GetPointerTo(m.Store, knxp) + knx := bs.Key.(*NameExpr) + ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, knx) ptr.TV.Assign(m.Alloc, iv, false) default: panic("should not happen") @@ -186,8 +186,8 @@ func (m *Machine) doOpExec(op Op) { case ASSIGN: m.PopAsPointer(bs.Value).Assign2(m.Alloc, m.Store, m.Realm, ev, false) case DEFINE: - vnxp := bs.Value.(*NameExpr).Path - ptr := m.LastBlock().GetPointerTo(m.Store, vnxp) + vnx := bs.Value.(*NameExpr) + ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, vnx) ptr.TV.Assign(m.Alloc, ev, false) default: panic("should not happen") @@ -267,8 +267,8 @@ func (m *Machine) doOpExec(op Op) { case ASSIGN: m.PopAsPointer(bs.Key).Assign2(m.Alloc, m.Store, m.Realm, iv, false) case DEFINE: - knxp := bs.Key.(*NameExpr).Path - ptr := m.LastBlock().GetPointerTo(m.Store, knxp) + knx := bs.Key.(*NameExpr) + ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, knx) ptr.TV.Assign(m.Alloc, iv, false) default: panic("should not happen") @@ -280,8 +280,8 @@ func (m *Machine) doOpExec(op Op) { case ASSIGN: m.PopAsPointer(bs.Value).Assign2(m.Alloc, m.Store, m.Realm, ev, false) case DEFINE: - vnxp := bs.Value.(*NameExpr).Path - ptr := m.LastBlock().GetPointerTo(m.Store, vnxp) + vnx := bs.Value.(*NameExpr) + ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, vnx) ptr.TV.Assign(m.Alloc, ev, false) default: panic("should not happen") @@ -360,8 +360,8 @@ func (m *Machine) doOpExec(op Op) { case ASSIGN: m.PopAsPointer(bs.Key).Assign2(m.Alloc, m.Store, m.Realm, kv, false) case DEFINE: - knxp := bs.Key.(*NameExpr).Path - ptr := m.LastBlock().GetPointerTo(m.Store, knxp) + knx := bs.Key.(*NameExpr) + ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, knx) ptr.TV.Assign(m.Alloc, kv, false) default: panic("should not happen") @@ -373,8 +373,8 @@ func (m *Machine) doOpExec(op Op) { case ASSIGN: m.PopAsPointer(bs.Value).Assign2(m.Alloc, m.Store, m.Realm, vv, false) case DEFINE: - vnxp := bs.Value.(*NameExpr).Path - ptr := m.LastBlock().GetPointerTo(m.Store, vnxp) + vnx := bs.Value.(*NameExpr) + ptr := m.LastBlock().GetPointerToMaybeHeapDefine(m.Store, vnx) ptr.TV.Assign(m.Alloc, vv, false) default: panic("should not happen") @@ -884,6 +884,8 @@ func (m *Machine) doOpTypeSwitch() { // NOTE: assumes the var is first in block. vp := NewValuePath( VPBlock, 1, 0, ss.VarName) + // NOTE: GetPointerToMaybeHeapDefine not needed, + // because this type is in new type switch clause block. ptr := b.GetPointerTo(m.Store, vp) ptr.TV.Assign(m.Alloc, *xv, false) } diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index 8ff0b5bd538..a1d677ca878 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -78,7 +78,7 @@ func (m *Machine) doOpIndex2() { func (m *Machine) doOpSelector() { sx := m.PopExpr().(*SelectorExpr) xv := m.PeekValue(1) - res := xv.GetPointerTo(m.Alloc, m.Store, sx.Path).Deref() + res := xv.GetPointerToFromTV(m.Alloc, m.Store, sx.Path).Deref() if debug { m.Printf("-v[S] %v\n", xv) m.Printf("+v[S] %v\n", res) @@ -758,6 +758,25 @@ func (m *Machine) doOpFuncLit() { ft := m.PopValue().V.(TypeValue).Type.(*FuncType) lb := m.LastBlock() m.Alloc.AllocateFunc() + + // First copy closure captured heap values + // to *FuncValue. Later during doOpCall a block + // will be created that copies these values for + // every invocation of the function. + captures := []TypedValue(nil) + for _, nx := range x.HeapCaptures { + ptr := lb.GetPointerTo(m.Store, nx.Path) + // check that ptr.TV is a heap item value. + // it must be in the form of: + // {T:heapItemType{},V:HeapItemValue{...}} + if _, ok := ptr.TV.T.(heapItemType); !ok { + panic("should not happen, should be heapItemType") + } + if _, ok := ptr.TV.V.(*HeapItemValue); !ok { + panic("should not happen, should be heapItemValue") + } + captures = append(captures, *ptr.TV) + } m.PushValue(TypedValue{ T: ft, V: &FuncValue{ @@ -766,6 +785,7 @@ func (m *Machine) doOpFuncLit() { Source: x, Name: "", Closure: lb, + Captures: captures, PkgPath: m.Package.PkgPath, body: x.Body, nativeBody: nil, diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 757cbbae317..e1dc3671333 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -2,8 +2,10 @@ package gnolang import ( "fmt" + "math" "math/big" "reflect" + "slices" "strings" "sync/atomic" @@ -23,7 +25,8 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) { // This will also reserve names on BlockNode.StaticBlock by // calling StaticBlock.Predefine(). for _, fn := range fset.Files { - SetNodeLocations(pn.PkgPath, string(fn.Name), fn) + setNodeLines(fn) + setNodeLocations(pn.PkgPath, string(fn.Name), fn) initStaticBlocks(store, pn, fn) } // NOTE: The calls to .Predefine() above is more of a name reservation, @@ -92,7 +95,7 @@ func PredefineFileSet(store Store, pn *PackageNode, fset *FileSet) { } } } - // Finally, predefine other decls and + // Then, predefine other decls and // preprocess ValueDecls.. for _, fn := range fset.Files { for i := 0; i < len(fn.Decls); i++ { @@ -143,32 +146,7 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { // iterate over all nodes recursively. _ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { - defer func() { - if r := recover(); r != nil { - // before re-throwing the error, append location information to message. - loc := last.GetLocation() - if nline := n.GetLine(); nline > 0 { - loc.Line = nline - } - - var err error - rerr, ok := r.(error) - if ok { - // NOTE: gotuna/gorilla expects error exceptions. - err = errors.Wrap(rerr, loc.String()) - } else { - // NOTE: gotuna/gorilla expects error exceptions. - err = fmt.Errorf("%s: %v", loc.String(), r) - } - - // Re-throw the error after wrapping it with the preprocessing stack information. - panic(&PreprocessError{ - err: err, - stack: stack, - }) - } - }() - + defer doRecover(stack, n) if debug { debug.Printf("initStaticBlocks %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) } @@ -181,45 +159,57 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { if n.Op == DEFINE { var defined bool for _, lx := range n.Lhs { - ln := lx.(*NameExpr).Name + nx := lx.(*NameExpr) + ln := nx.Name if ln == blankIdentifier { continue } - last.Predefine(false, ln) - defined = true + if !isLocallyDefined2(last, ln) { + // if loop extern, will change to + // NameExprTypeHeapDefine later. + nx.Type = NameExprTypeDefine + last.Predefine(false, ln) + defined = true + } } if !defined { panic(fmt.Sprintf("nothing defined in assignment %s", n.String())) } } case *ImportDecl: - name := n.Name - if name == "." { + nx := &n.NameExpr + nn := nx.Name + if nn == "." { panic("dot imports not allowed in gno") } - if name == "" { // use default + if nn == "" { // use default pv := store.GetPackage(n.PkgPath, true) if pv == nil { panic(fmt.Sprintf( "unknown import path %s", n.PkgPath)) } - name = pv.PkgName + nn = pv.PkgName } - if name != blankIdentifier { - last.Predefine(false, name) + if nn != blankIdentifier { + nx.Type = NameExprTypeDefine + last.Predefine(false, nn) } case *ValueDecl: last2 := skipFile(last) for i := 0; i < len(n.NameExprs); i++ { nx := &n.NameExprs[i] - if nx.Name == blankIdentifier { + nn := nx.Name + if nn == blankIdentifier { continue } - last2.Predefine(n.Const, nx.Name) + nx.Type = NameExprTypeDefine + last2.Predefine(n.Const, nn) } case *TypeDecl: last2 := skipFile(last) + nx := &n.NameExpr + nx.Type = NameExprTypeDefine last2.Predefine(false, n.Name) case *FuncDecl: if n.IsMethod { @@ -238,7 +228,8 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { dname := Name(fmt.Sprintf("init.%d", idx)) n.Name = dname } - + nx := &n.NameExpr + nx.Type = NameExprTypeDefine pkg.Predefine(false, n.Name) } case *FuncTypeExpr: @@ -256,7 +247,7 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { if r.Name == blankIdentifier { // create a hidden var with leading dot. // NOTE: document somewhere. - rn := fmt.Sprintf(".res_%d", i) + rn := fmt.Sprintf("%s%d", hiddenResultVariable, i) r.Name = Name(rn) } } @@ -265,15 +256,9 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { // ---------------------------------------- case TRANS_BLOCK: + pushInitBlock(n.(BlockNode), &last, &stack) switch n := n.(type) { - case *BlockStmt: - pushInitBlock(n, &last, &stack) - case *ForStmt: - pushInitBlock(n, &last, &stack) - case *IfStmt: - pushInitBlock(n, &last, &stack) case *IfCaseStmt: - pushInitRealBlock(n, &last, &stack) // parent if statement. ifs := ns[len(ns)-1].(*IfStmt) // anything declared in ifs are copied. @@ -281,17 +266,27 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { last.Predefine(false, n) } case *RangeStmt: - pushInitBlock(n, &last, &stack) if n.Op == DEFINE { if n.Key != nil { - last.Predefine(false, n.Key.(*NameExpr).Name) + nx := n.Key.(*NameExpr) + if nx.Name != blankIdentifier { + // XXX, this should be uncommented when fully + // support Go1.22 loopvar, to make it consistent + // with for i := 0; i < 10; i++ {...}. + // nx.Type = NameExprTypeDefine + + last.Predefine(false, nx.Name) + } } if n.Value != nil { - last.Predefine(false, n.Value.(*NameExpr).Name) + nx := n.Value.(*NameExpr) + if nx.Name != blankIdentifier { + // nx.Type = NameExprTypeDefine // XXX,ditto + last.Predefine(false, nx.Name) + } } } case *FuncLitExpr: - pushInitBlock(n, &last, &stack) for _, p := range n.Type.Params { last.Predefine(false, p.Name) } @@ -300,10 +295,7 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { last.Predefine(false, rf.Name) } } - case *SelectCaseStmt: - pushInitBlock(n, &last, &stack) case *SwitchStmt: - pushInitBlock(n, &last, &stack) if n.VarName != "" { // NOTE: this defines for default clauses too, // see comment on block copying @ @@ -311,7 +303,6 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { last.Predefine(false, n.VarName) } case *SwitchClauseStmt: - pushInitRealBlock(n, &last, &stack) // parent switch statement. ss := ns[len(ns)-1].(*SwitchStmt) // anything declared in ss are copied, @@ -329,7 +320,6 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { } } case *FuncDecl: - pushInitBlock(n, &last, &stack) if n.IsMethod { n.Predefine(false, n.Recv.Name) } @@ -339,23 +329,24 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { } n.Predefine(false, pte.Name) } - for _, rte := range n.Type.Results { - if rte.Name != "" { - n.Predefine(false, rte.Name) + for i, rte := range n.Type.Results { + if rte.Name == "" { + rn := fmt.Sprintf("%s%d", hiddenResultVariable, i) + rte.Name = Name(rn) } + n.Predefine(false, rte.Name) } - case *FileNode: - pushInitBlock(n, &last, &stack) - default: - panic("should not happen") } return n, TRANS_CONTINUE // ---------------------------------------- case TRANS_LEAVE: - // finalization. - if _, ok := n.(BlockNode); ok { - // Pop block. + // Pop block from stack. + // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK + // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR + // POP BLOCK YOURSELF. + switch n.(type) { + case BlockNode: stack = stack[:len(stack)-1] last = stack[len(stack)-1] } @@ -365,6 +356,34 @@ func initStaticBlocks(store Store, ctx BlockNode, bn BlockNode) { }) } +func doRecover(stack []BlockNode, n Node) { + if r := recover(); r != nil { + // before re-throwing the error, append location information to message. + last := stack[len(stack)-1] + loc := last.GetLocation() + if nline := n.GetLine(); nline > 0 { + loc.Line = nline + loc.Column = n.GetColumn() + } + + var err error + rerr, ok := r.(error) + if ok { + // NOTE: gotuna/gorilla expects error exceptions. + err = errors.Wrap(rerr, loc.String()) + } else { + // NOTE: gotuna/gorilla expects error exceptions. + err = fmt.Errorf("%s: %v", loc.String(), r) + } + + // Re-throw the error after wrapping it with the preprocessing stack information. + panic(&PreprocessError{ + err: err, + stack: stack, + }) + } +} + // This counter ensures (during testing) that certain functions // (like ConvertUntypedTo() for bigints and strings) // are only called during the preprocessing stage. @@ -390,6 +409,27 @@ var preprocessing atomic.Int32 // - Assigns BlockValuePath to NameExprs. // - TODO document what it does. func Preprocess(store Store, ctx BlockNode, n Node) Node { + // If initStaticBlocks doesn't happen here, + // it means Preprocess on blocks might fail. + // it works for now because preprocess also does pushInitBlock, + // but it's kinda weird. + // maybe consider moving initStaticBlocks here and ensure idempotency of it. + n = preprocess1(store, ctx, n) + // XXX check node lines and locations + checkNodeLinesLocations("XXXpkgPath", "XXXfileName", n) + // XXX what about the fact that preprocess1 sets the PREPROCESSED attr on all nodes? + // XXX do any of the following need the attr, or similar attrs? + // XXX well the following may be isn't idempotent, + // XXX so it is currently strange. + if bn, ok := n.(BlockNode); ok { + findGotoLoopDefines(ctx, bn) + findLoopUses1(ctx, bn) + findLoopUses2(ctx, bn) + } + return n +} + +func preprocess1(store Store, ctx BlockNode, n Node) Node { // Increment preprocessing counter while preprocessing. preprocessing.Add(1) defer preprocessing.Add(-1) @@ -403,7 +443,8 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if fn, ok := n.(*FileNode); ok { pkgPath := ctx.(*PackageNode).PkgPath fileName := string(fn.Name) - SetNodeLocations(pkgPath, fileName, fn) + setNodeLines(fn) + setNodeLocations(pkgPath, fileName, fn) } // create stack of BlockNodes. @@ -419,32 +460,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { return n, TRANS_SKIP } - defer func() { - if r := recover(); r != nil { - // before re-throwing the error, append location information to message. - loc := last.GetLocation() - if nline := n.GetLine(); nline > 0 { - loc.Line = nline - loc.Column = n.GetColumn() - } - - var err error - rerr, ok := r.(error) - if ok { - // NOTE: gotuna/gorilla expects error exceptions. - err = errors.Wrap(rerr, loc.String()) - } else { - // NOTE: gotuna/gorilla expects error exceptions. - err = fmt.Errorf("%s: %v", loc.String(), r) - } - - // Re-throw the error after wrapping it with the preprocessing stack information. - panic(&PreprocessError{ - err: err, - stack: stack, - }) - } - }() + defer doRecover(stack, n) if debug { debug.Printf("Preprocess %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) } @@ -540,7 +556,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *IfCaseStmt: - pushInitRealBlockAndCopy(n, &last, &stack) + pushInitBlockAndCopy(n, &last, &stack) // parent if statement. ifs := ns[len(ns)-1].(*IfStmt) // anything declared in ifs are copied. @@ -624,7 +640,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } else { // create a hidden var with leading dot. // NOTE: document somewhere. - rn := fmt.Sprintf(".res_%d", i) + rn := fmt.Sprintf("%s%d", hiddenResultVariable, i) last.Define(Name(rn), anyValue(rf.Type)) } } @@ -656,7 +672,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // TRANS_BLOCK ----------------------- case *SwitchClauseStmt: - pushInitRealBlockAndCopy(n, &last, &stack) + pushInitBlockAndCopy(n, &last, &stack) // parent switch statement. ss := ns[len(ns)-1].(*SwitchStmt) // anything declared in ss are copied, @@ -750,7 +766,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { last.Define(rf.Name, anyValue(rf.Type)) } else { // create a hidden var with leading dot. - rn := fmt.Sprintf(".res_%d", i) + rn := fmt.Sprintf("%s%d", hiddenResultVariable, i) last.Define(Name(rn), anyValue(rf.Type)) } } @@ -869,15 +885,23 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // in evalStaticType(store,). n.SetAttribute(ATTR_PREPROCESSED, true) - // -There is still work to be done while leaving, but - // once the logic of that is done, we will have to - // perform additionally deferred logic that is best - // handled with orthogonal switch conditions. - // -For example, while leaving nodes w/ - // TRANS_COMPOSITE_TYPE, (regardless of whether name or - // literal), any elided type names are inserted. (This - // works because the transcriber leaves the composite - // type before entering the kv elements.) + // Defer pop block from stack. + // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK + // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR + // POP BLOCK YOURSELF. + defer func() { + switch n.(type) { + case BlockNode: + stack = stack[:len(stack)-1] + last = stack[len(stack)-1] + } + }() + + // While leaving nodes w/ TRANS_COMPOSITE_TYPE, + // (regardless of whether name or literal), any elided + // type names are inserted. (This works because the + // transcriber leaves the composite type before + // entering the kv elements.) defer func() { switch ftype { // TRANS_LEAVE (deferred)--------- @@ -963,6 +987,10 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { if ftype == TRANS_ASSIGN_LHS { as := ns[len(ns)-1].(*AssignStmt) fillNameExprPath(last, n, as.Op == DEFINE) + return n, TRANS_CONTINUE + } else if ftype == TRANS_VAR_NAME { + fillNameExprPath(last, n, true) + return n, TRANS_CONTINUE } else { fillNameExprPath(last, n, false) } @@ -1389,7 +1417,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // convert to byteslice. args1 := n.Args[1] if evalStaticTypeOf(store, last, args1).Kind() == StringKind { - bsx := constType(nil, gByteSliceType) + bsx := constType(n, gByteSliceType) args1 = Call(bsx, args1) args1 = Preprocess(nil, last, args1).(Expr) n.Args[1] = args1 @@ -1424,7 +1452,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { // convert to byteslice. args1 := n.Args[1] if evalStaticTypeOf(store, last, args1).Kind() == StringKind { - bsx := constType(nil, gByteSliceType) + bsx := constType(n, gByteSliceType) args1 = Call(bsx, args1) args1 = Preprocess(nil, last, args1).(Expr) n.Args[1] = args1 @@ -2152,6 +2180,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { panic("should not happen") } + // TRANS_LEAVE ----------------------- case *IncDecStmt: xt := evalStaticTypeOf(store, last, n.X) n.AssertCompatible(xt) @@ -2479,13 +2508,6 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } // end type switch statement // END TRANS_LEAVE ----------------------- - - // finalization (during leave). - if _, ok := n.(BlockNode); ok { - // Pop block. - stack = stack[:len(stack)-1] - last = stack[len(stack)-1] - } return n, TRANS_CONTINUE } @@ -2496,6 +2518,458 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { return nn } +// Identifies NameExprTypeHeapDefines. +// Also finds GotoLoopStmts, XXX but probably remove, not needed. +func findGotoLoopDefines(ctx BlockNode, bn BlockNode) { + // create stack of BlockNodes. + var stack []BlockNode = make([]BlockNode, 0, 32) + var last BlockNode = ctx + stack = append(stack, last) + + // iterate over all nodes recursively. + _ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + defer doRecover(stack, n) + + if debug { + debug.Printf("findGotoLoopDefines %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) + } + + switch stage { + // ---------------------------------------- + case TRANS_ENTER: + return n, TRANS_CONTINUE + + // ---------------------------------------- + case TRANS_BLOCK: + pushInitBlock(n.(BlockNode), &last, &stack) + return n, TRANS_CONTINUE + + // ---------------------------------------- + case TRANS_LEAVE: + + // Defer pop block from stack. + // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK + // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR + // POP BLOCK YOURSELF. + defer func() { + switch n.(type) { + case BlockNode: + stack = stack[:len(stack)-1] + last = stack[len(stack)-1] + } + }() + + switch n := n.(type) { + case *ForStmt, *RangeStmt: + Transcribe(n, + func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + switch stage { + case TRANS_ENTER: + switch n := n.(type) { + case *FuncLitExpr: + // inner funcs. + return n, TRANS_SKIP + case *FuncDecl: + panic("unexpected inner func decl") + case *NameExpr: + if n.Type == NameExprTypeDefine { + n.Type = NameExprTypeHeapDefine + } + } + } + return n, TRANS_CONTINUE + }) + case *BranchStmt: + switch n.Op { + case GOTO: + bn, _, _ := findGotoLabel(last, n.Label) + // already done in Preprocess: + // n.Depth = depth + // n.BodyIndex = index + + // NOTE: we must not use line numbers + // for logic, as line numbers are not + // guaranteed (see checkNodeLinesLocations). + // Instead we rely on the transcribe order + // and keep track of whether we've seen + // the label and goto stmts respectively. + // + // DOES NOT WORK: + // gotoLine := n.GetLine() + // if labelLine >= gotoLine { + // return n, TRANS_SKIP + // } + var ( + label = n.Label + labelReached bool + origGoto = n + ) + + // Recurse and mark stmts as ATTR_GOTOLOOP_STMT. + // NOTE: ATTR_GOTOLOOP_STMT is not used. + Transcribe(bn, + func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + switch stage { + case TRANS_ENTER: + // Check to see if label reached. + if _, ok := n.(Stmt); ok { + // XXX HasLabel + if n.GetLabel() == label { + labelReached = true + } + // If goto < label, + // then not a goto loop. + if n == origGoto && !labelReached { + return n, TRANS_EXIT + } + } + + // If label not reached, continue. + if !labelReached { + return n, TRANS_CONTINUE + } + + // NOTE: called redundantly + // for many goto stmts, + // idempotenct updates only. + switch n := n.(type) { + // Skip the body of these: + case *FuncLitExpr: + if len(ns) > 0 { + // inner funcs. + return n, TRANS_SKIP + } + return n, TRANS_CONTINUE + case *FuncDecl: + if len(ns) > 0 { + panic("unexpected inner func decl") + } + return n, TRANS_CONTINUE + // Otherwise mark stmt as gotoloop. + case Stmt: + // we're done if we + // re-encounter origGotoStmtm. + if n == origGoto { + n.SetAttribute( + ATTR_GOTOLOOP_STMT, + true) + return n, TRANS_EXIT // done + } + // otherwise set attribute. + n.SetAttribute( + ATTR_GOTOLOOP_STMT, + true) + return n, TRANS_CONTINUE + // Special case, maybe convert + // NameExprTypeDefine to + // NameExprTypeHeapDefine. + case *NameExpr: + if n.Type == NameExprTypeDefine { + n.Type = NameExprTypeHeapDefine + } + } + return n, TRANS_CONTINUE + } + return n, TRANS_CONTINUE + }) + } + } + return n, TRANS_CONTINUE + } + return n, TRANS_CONTINUE + }) +} + +// Find uses of loop names; those names that are defined as loop defines; +// defines within loops that are used as reference or captured in a closure +// later. Also happens to adjust the type (but not paths) of such usage. +// If there is no usage of the &name or as closure capture, a +// NameExprTypeHeapDefine gets demoted to NameExprTypeDefine in demoteHeapDefines(). +func findLoopUses1(ctx BlockNode, bn BlockNode) { + // create stack of BlockNodes. + var stack []BlockNode = make([]BlockNode, 0, 32) + var last BlockNode = ctx + stack = append(stack, last) + + // Iterate over all nodes recursively. + _ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + defer doRecover(stack, n) + + if debug { + debug.Printf("findLoopUses1 %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) + } + + switch stage { + // ---------------------------------------- + case TRANS_BLOCK: + pushInitBlock(n.(BlockNode), &last, &stack) + + // ---------------------------------------- + case TRANS_ENTER: + switch n := n.(type) { + case *NameExpr: + // Ignore non-block type paths + if n.Path.Type != VPBlock { + return n, TRANS_CONTINUE + } + switch n.Type { + case NameExprTypeNormal: + // Find the block where name is defined + dbn := last.GetBlockNodeForPath(nil, n.Path) + // if the name is loop defined, + lds, _ := dbn.GetAttribute(ATTR_LOOP_DEFINES).([]Name) + if slices.Contains(lds, n.Name) { + fle, depth, found := findFirstClosure(stack, dbn) + if found { + // If across a closure, + // mark name as loop used. + addAttrHeapUse(dbn, n.Name) + // The path must stay same for now, + // used later in findLoopUses2. + idx := addHeapCapture(dbn, fle, n.Name) + // adjust NameExpr type. + n.Type = NameExprTypeHeapUse + n.Path.Depth = uint8(depth) + n.Path.Index = idx + } else { + if ftype == TRANS_REF_X { + // if used as a reference, + // mark name as loop used. + addAttrHeapUse(dbn, n.Name) + // Also adjust NameExpr type. + // We could do this later too. + n.Type = NameExprTypeHeapUse + } + } + } else { + // if the name is not loop defined, + // do nothing. + } + case NameExprTypeDefine: + // nothing to do. + case NameExprTypeHeapDefine: + // Set name in attribute, so later matches + // on NameExpr can know that this was loop defined + // on this block. + setAttrHeapDefine(last, n.Name) + case NameExprTypeHeapUse, NameExprTypeHeapClosure: + panic("unexpected node type") + } + } + return n, TRANS_CONTINUE + + // ---------------------------------------- + case TRANS_LEAVE: + // Pop block from stack. + // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK + // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR + // POP BLOCK YOURSELF. + switch n.(type) { + case BlockNode: + stack = stack[:len(stack)-1] + last = stack[len(stack)-1] + } + + return n, TRANS_CONTINUE + } + return n, TRANS_CONTINUE + }) +} + +func assertNotHasName(names []Name, name Name) { + if slices.Contains(names, name) { + panic(fmt.Sprintf("name: %s already contained in names: %v", name, names)) + } +} + +func setAttrHeapDefine(bn BlockNode, name Name) { + bnLDs, _ := bn.GetAttribute(ATTR_LOOP_DEFINES).([]Name) + assertNotHasName(bnLDs, name) + bnLDs = append(bnLDs, name) + bn.SetAttribute(ATTR_LOOP_DEFINES, bnLDs) +} + +func addAttrHeapUse(bn BlockNode, name Name) { + bnLUs, _ := bn.GetAttribute(ATTR_LOOP_USES).([]Name) + if slices.Contains(bnLUs, name) { + return + } else { + bnLUs = append(bnLUs, name) + bn.SetAttribute(ATTR_LOOP_USES, bnLUs) + return + } +} + +// adds ~name to fle static block and to heap captures atomically. +func addHeapCapture(dbn BlockNode, fle *FuncLitExpr, name Name) (idx uint16) { + for _, ne := range fle.HeapCaptures { + if ne.Name == name { + // assert ~name also already defined. + var ok bool + idx, ok = fle.GetLocalIndex("~" + name) + if !ok { + panic("~name not added to fle atomically") + } + return // already exists + } + } + + // define ~name to fle. + _, ok := fle.GetLocalIndex("~" + name) + if ok { + panic("~name already defined in fle") + } + + tv := dbn.GetValueRef(nil, name, true) + fle.Define("~"+name, tv.Copy(nil)) + + // add name to fle.HeapCaptures. + vp := fle.GetPathForName(nil, name) + vp.Depth -= 1 // minus 1 for fle itself. + ne := NameExpr{ + Path: vp, + Name: name, + Type: NameExprTypeHeapClosure, + } + fle.HeapCaptures = append(fle.HeapCaptures, ne) + + // find index after define + for i, n := range fle.GetBlockNames() { + if n == "~"+name { + idx = uint16(i) + return + } + } + + panic("should not happen, idx not found") +} + +// finds the first FuncLitExpr in the stack at or after stop. +// returns the depth of first closure, 1 if stop itself is a closure, +// or 0 if not found. +func findFirstClosure(stack []BlockNode, stop BlockNode) (fle *FuncLitExpr, depth int, found bool) { + redundant := 0 // count faux block + for i := len(stack) - 1; i >= 0; i-- { + stbn := stack[i] + switch stbn := stbn.(type) { + case *FuncLitExpr: + if stbn == stop { // if fle is stopBn, does not count, use last fle + return + } + fle = stbn + depth = len(stack) - 1 - redundant - i + 1 // +1 since 1 is lowest. + found = true + // even if found, continue iteration in case + // an earlier *FuncLitExpr is found. + case *IfCaseStmt, *SwitchClauseStmt: + if stbn == stop { + return + } + redundant++ + default: + if stbn == stop { + return + } + } + } + panic("stop not found in stack") +} + +// Convert non-loop uses of loop names to NameExprTypeHeapUse. +// Also, NameExprTypeHeapDefine gets demoted to NameExprTypeDefine if no actual +// usage was found that warrants a NameExprTypeHeapDefine. +func findLoopUses2(ctx BlockNode, bn BlockNode) { + // create stack of BlockNodes. + var stack []BlockNode = make([]BlockNode, 0, 32) + var last BlockNode = ctx + stack = append(stack, last) + + // Iterate over all nodes recursively. + _ = Transcribe(bn, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + defer doRecover(stack, n) + + if debug { + debug.Printf("findLoopUses2 %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) + } + + switch stage { + // ---------------------------------------- + case TRANS_BLOCK: + pushInitBlock(n.(BlockNode), &last, &stack) + + // ---------------------------------------- + case TRANS_ENTER: + switch n := n.(type) { + case *NameExpr: + // Ignore non-block type paths + if n.Path.Type != VPBlock { + return n, TRANS_CONTINUE + } + switch n.Type { + case NameExprTypeNormal: + // Find the block where name is defined + dbn := last.GetBlockNodeForPath(nil, n.Path) + // if the name is loop defined, + lds, _ := dbn.GetAttribute(ATTR_LOOP_DEFINES).([]Name) + if slices.Contains(lds, n.Name) { + // if the name is actually loop used, + lus, _ := dbn.GetAttribute(ATTR_LOOP_USES).([]Name) + if slices.Contains(lus, n.Name) { + // change type finally to HeapUse. + n.Type = NameExprTypeHeapUse + } else { + // else, will be demoted in later clause. + } + } + case NameExprTypeHeapDefine: + // Find the block where name is defined + dbn := last.GetBlockNodeForPath(nil, n.Path) + // if the name is loop defined + lds, _ := dbn.GetAttribute(ATTR_LOOP_DEFINES).([]Name) + if slices.Contains(lds, n.Name) { + // if the name is actually loop used + lus, _ := dbn.GetAttribute(ATTR_LOOP_USES).([]Name) + if !slices.Contains(lus, n.Name) { + // demote type finally to Define. + n.Type = NameExprTypeDefine + } + } + } + } + return n, TRANS_CONTINUE + + // ---------------------------------------- + case TRANS_LEAVE: + + // Defer pop block from stack. + // NOTE: DO NOT USE TRANS_SKIP WITHIN BLOCK + // NODES, AS TRANS_LEAVE WILL BE SKIPPED; OR + // POP BLOCK YOURSELF. + defer func() { + switch n.(type) { + case BlockNode: + stack = stack[:len(stack)-1] + last = stack[len(stack)-1] + } + }() + + switch n := n.(type) { + case BlockNode: + lds, _ := n.GetAttribute(ATTR_LOOP_DEFINES).([]Name) + lus, _ := n.GetAttribute(ATTR_LOOP_USES).([]Name) + if len(lds) < len(lus) { + panic("defines should be a superset of used-defines") + } + // no need anymore + n.DelAttribute(ATTR_LOOP_USES) + n.DelAttribute(ATTR_LOOP_DEFINES) + } + return n, TRANS_CONTINUE + } + return n, TRANS_CONTINUE + }) +} + func isSwitchLabel(ns []Node, label Name) bool { for { swch := lastSwitch(ns) @@ -2514,37 +2988,38 @@ func isSwitchLabel(ns []Node, label Name) bool { } // Idempotent. +// Also makes sure the stack doesn't reach MaxUint8 in length. func pushInitBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { if !bn.IsInitialized() { - bn.InitStaticBlock(bn, *last) + switch bn.(type) { + case *IfCaseStmt, *SwitchClauseStmt: + // skip faux block + bn.InitStaticBlock(bn, (*last).GetParentNode(nil)) + default: + bn.InitStaticBlock(bn, *last) + } } if bn.GetStaticBlock().Source != bn { panic("expected the source of a block node to be itself") } *last = bn *stack = append(*stack, bn) -} - -// like pushInitBlock(), but when the last block is a faux block, -// namely after SwitchStmt and IfStmt. -// Idempotent. -func pushInitRealBlock(bn BlockNode, last *BlockNode, stack *[]BlockNode) { - if !bn.IsInitialized() { - bn.InitStaticBlock(bn, (*last).GetParentNode(nil)) - } - if bn.GetStaticBlock().Source != bn { - panic("expected the source of a block node to be itself") + if len(*stack) >= math.MaxUint8 { + panic("block node depth reached maximum MaxUint8") } - *last = bn - *stack = append(*stack, bn) } // like pushInitBlock(), but when the last block is a faux block, // namely after SwitchStmt and IfStmt. // Not idempotent, as it calls bn.Define with reference to last's TV value slot. -func pushInitRealBlockAndCopy(bn BlockNode, last *BlockNode, stack *[]BlockNode) { +func pushInitBlockAndCopy(bn BlockNode, last *BlockNode, stack *[]BlockNode) { + if _, ok := bn.(*IfCaseStmt); !ok { + if _, ok := bn.(*SwitchClauseStmt); !ok { + panic("should not happen") + } + } orig := *last - pushInitRealBlock(bn, last, stack) + pushInitBlock(bn, last, stack) copyFromFauxBlock(bn, orig) } @@ -2750,6 +3225,7 @@ func evalConst(store Store, last BlockNode, x Expr) *ConstExpr { Source: x, TypedValue: cv, } + cx.SetLine(x.GetLine()) cx.SetAttribute(ATTR_PREPROCESSED, true) setConstAttrs(cx) return cx @@ -2758,6 +3234,7 @@ func evalConst(store Store, last BlockNode, x Expr) *ConstExpr { func constType(source Expr, t Type) *constTypeExpr { cx := &constTypeExpr{Source: source} cx.Type = t + cx.SetLine(source.GetLine()) cx.SetAttribute(ATTR_PREPROCESSED, true) return cx } @@ -3060,7 +3537,7 @@ func convertType(store Store, last BlockNode, x *Expr, t Type) { // convert x to destination type t func doConvertType(store Store, last BlockNode, x *Expr, t Type) { - cx := Expr(Call(constType(nil, t), *x)) + cx := Expr(Call(constType(*x, t), *x)) cx = Preprocess(store, last, cx).(Expr) *x = cx } @@ -3296,7 +3773,7 @@ func findUndefined2(store Store, last BlockNode, x Expr, t Type) (un Name) { panic("cannot elide unknown composite type") } ct = t - cx.Type = constType(nil, t) + cx.Type = constType(cx, t) } else { un = findUndefined(store, last, cx.Type) if un != "" { @@ -3880,10 +4357,6 @@ func fillNameExprPath(last BlockNode, nx *NameExpr, isDefineLHS bool) { } // If not DEFINE_LHS, yet is statically undefined, set path from parent. - - // NOTE: ValueDecl names don't need this distinction, as - // the transcriber doesn't enter any of the ValueDecl.NameExprs nodes, - // so this function never gets called for them. if !isDefineLHS { if last.GetStaticTypeOf(nil, nx.Name) == nil { // NOTE: We cannot simply call last.GetPathForName() as below here, @@ -4215,13 +4688,38 @@ func isLocallyDefined(bn BlockNode, n Name) bool { return true } +// r := 0 +// r, ok := 1, true +func isLocallyDefined2(bn BlockNode, n Name) bool { + _, isLocal := bn.GetLocalIndex(n) + return isLocal +} + // ---------------------------------------- -// SetNodeLocations +// setNodeLines & setNodeLocations -// Iterate over all block nodes recursively and sets location information -// based on sparse expectations, and ensures uniqueness of BlockNode.Locations. +func setNodeLines(n Node) { + lastLine := 0 + Transcribe(n, func(ns []Node, ftype TransField, index int, n Node, stage TransStage) (Node, TransCtrl) { + if stage != TRANS_ENTER { + return n, TRANS_CONTINUE + } + line := n.GetLine() + if line == lastLine { + } else if line == 0 { + line = lastLine + } else { + lastLine = line + } + n.SetLine(line) + return n, TRANS_CONTINUE + }) +} + +// Iterate over all nodes recursively and sets location information +// based on sparse expectations on block nodes, and ensures uniqueness of BlockNode.Locations. // Ensures uniqueness of BlockNode.Locations. -func SetNodeLocations(pkgPath string, fileName string, n Node) { +func setNodeLocations(pkgPath string, fileName string, n Node) { if n.GetAttribute(ATTR_LOCATIONED) == true { return // locations already set (typically n is a filenode). } @@ -4246,6 +4744,13 @@ func SetNodeLocations(pkgPath string, fileName string, n Node) { }) } +// XXX check node lines, uniqueness of locations, +// and also check location pkgpath and filename. +// Even after this is implemented, locations should not be used for logic. +func checkNodeLinesLocations(pkgPath string, fileName string, n Node) { + // TODO: XXX +} + // ---------------------------------------- // SaveBlockNodes diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 3710524130a..5913f13a0f7 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -845,6 +845,9 @@ func getChildObjects(val Value, more []Value) []Value { if bv, ok := cv.Closure.(*Block); ok { more = getSelfOrChildObjects(bv, more) } + for _, c := range cv.Captures { + more = getSelfOrChildObjects(c.V, more) + } return more case *BoundMethodValue: more = getChildObjects(cv.Func, more) // *FuncValue not object @@ -1142,6 +1145,7 @@ func copyValueWithRefs(val Value) Value { Source: source, Name: cv.Name, Closure: closure, + Captures: cv.Captures, FileName: cv.FileName, PkgPath: cv.PkgPath, NativePkg: cv.NativePkg, diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go index 0e6d89a7bf3..9913c434282 100644 --- a/gnovm/pkg/gnolang/store.go +++ b/gnovm/pkg/gnolang/store.go @@ -47,7 +47,7 @@ type Store interface { GetTypeSafe(tid TypeID) Type SetCacheType(Type) SetType(Type) - GetBlockNode(Location) BlockNode + GetBlockNode(Location) BlockNode // to get a PackageNode, use PackageNodeLocation(). GetBlockNodeSafe(Location) BlockNode SetBlockNode(BlockNode) // UNSTABLE diff --git a/gnovm/pkg/gnolang/transcribe.go b/gnovm/pkg/gnolang/transcribe.go index 28e97ff2b5b..dab539a8707 100644 --- a/gnovm/pkg/gnolang/transcribe.go +++ b/gnovm/pkg/gnolang/transcribe.go @@ -47,6 +47,7 @@ const ( TRANS_COMPOSITE_KEY TRANS_COMPOSITE_VALUE TRANS_FUNCLIT_TYPE + TRANS_FUNCLIT_HEAP_CAPTURE TRANS_FUNCLIT_BODY TRANS_FIELDTYPE_TYPE TRANS_FIELDTYPE_TAG @@ -100,6 +101,7 @@ const ( TRANS_IMPORT_PATH TRANS_CONST_TYPE TRANS_CONST_VALUE + TRANS_VAR_NAME // XXX stringer TRANS_VAR_TYPE TRANS_VAR_VALUE TRANS_TYPE_TYPE @@ -112,6 +114,7 @@ const ( // ENTER,CHILDS1,[BLOCK,CHILDS2]?,LEAVE sequence for that node, // i.e. skipping (the rest of) it; // - TRANS_BREAK to break out of looping in CHILDS1 or CHILDS2, +// but still perform TRANS_LEAVE. // - TRANS_EXIT to stop traversing altogether. // // Do not mutate ns. @@ -264,6 +267,14 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc if isStopOrSkip(nc, c) { return } + for idx := range cnn.HeapCaptures { + cnn.HeapCaptures[idx] = *(transcribe(t, nns, TRANS_FUNCLIT_HEAP_CAPTURE, idx, &cnn.HeapCaptures[idx], &c).(*NameExpr)) + if isBreak(c) { + break + } else if isStopOrSkip(nc, c) { + return + } + } cnn2, c2 := t(ns, ftype, index, cnn, TRANS_BLOCK) if isStopOrSkip(nc, c2) { nn = cnn2 @@ -692,6 +703,10 @@ func transcribe(t Transform, ns []Node, ftype TransField, index int, n Node, nc return } } + // XXX consider RHS, LHS, RHS, LHS, ... order. + for idx := range cnn.NameExprs { + cnn.NameExprs[idx] = *(transcribe(t, nns, TRANS_VAR_NAME, idx, &cnn.NameExprs[idx], &c).(*NameExpr)) + } for idx := range cnn.Values { cnn.Values[idx] = transcribe(t, nns, TRANS_VAR_VALUE, idx, cnn.Values[idx], &c).(Expr) if isBreak(c) { diff --git a/gnovm/pkg/gnolang/transfield_string.go b/gnovm/pkg/gnolang/transfield_string.go index 587ca7a7654..31afcf2be0d 100644 --- a/gnovm/pkg/gnolang/transfield_string.go +++ b/gnovm/pkg/gnolang/transfield_string.go @@ -29,68 +29,70 @@ func _() { _ = x[TRANS_COMPOSITE_KEY-18] _ = x[TRANS_COMPOSITE_VALUE-19] _ = x[TRANS_FUNCLIT_TYPE-20] - _ = x[TRANS_FUNCLIT_BODY-21] - _ = x[TRANS_FIELDTYPE_TYPE-22] - _ = x[TRANS_FIELDTYPE_TAG-23] - _ = x[TRANS_ARRAYTYPE_LEN-24] - _ = x[TRANS_ARRAYTYPE_ELT-25] - _ = x[TRANS_SLICETYPE_ELT-26] - _ = x[TRANS_INTERFACETYPE_METHOD-27] - _ = x[TRANS_CHANTYPE_VALUE-28] - _ = x[TRANS_FUNCTYPE_PARAM-29] - _ = x[TRANS_FUNCTYPE_RESULT-30] - _ = x[TRANS_MAPTYPE_KEY-31] - _ = x[TRANS_MAPTYPE_VALUE-32] - _ = x[TRANS_STRUCTTYPE_FIELD-33] - _ = x[TRANS_MAYBENATIVETYPE_TYPE-34] - _ = x[TRANS_ASSIGN_LHS-35] - _ = x[TRANS_ASSIGN_RHS-36] - _ = x[TRANS_BLOCK_BODY-37] - _ = x[TRANS_DECL_BODY-38] - _ = x[TRANS_DEFER_CALL-39] - _ = x[TRANS_EXPR_X-40] - _ = x[TRANS_FOR_INIT-41] - _ = x[TRANS_FOR_COND-42] - _ = x[TRANS_FOR_POST-43] - _ = x[TRANS_FOR_BODY-44] - _ = x[TRANS_GO_CALL-45] - _ = x[TRANS_IF_INIT-46] - _ = x[TRANS_IF_COND-47] - _ = x[TRANS_IF_BODY-48] - _ = x[TRANS_IF_ELSE-49] - _ = x[TRANS_IF_CASE_BODY-50] - _ = x[TRANS_INCDEC_X-51] - _ = x[TRANS_RANGE_X-52] - _ = x[TRANS_RANGE_KEY-53] - _ = x[TRANS_RANGE_VALUE-54] - _ = x[TRANS_RANGE_BODY-55] - _ = x[TRANS_RETURN_RESULT-56] - _ = x[TRANS_PANIC_EXCEPTION-57] - _ = x[TRANS_SELECT_CASE-58] - _ = x[TRANS_SELECTCASE_COMM-59] - _ = x[TRANS_SELECTCASE_BODY-60] - _ = x[TRANS_SEND_CHAN-61] - _ = x[TRANS_SEND_VALUE-62] - _ = x[TRANS_SWITCH_INIT-63] - _ = x[TRANS_SWITCH_X-64] - _ = x[TRANS_SWITCH_CASE-65] - _ = x[TRANS_SWITCHCASE_CASE-66] - _ = x[TRANS_SWITCHCASE_BODY-67] - _ = x[TRANS_FUNC_RECV-68] - _ = x[TRANS_FUNC_TYPE-69] - _ = x[TRANS_FUNC_BODY-70] - _ = x[TRANS_IMPORT_PATH-71] - _ = x[TRANS_CONST_TYPE-72] - _ = x[TRANS_CONST_VALUE-73] - _ = x[TRANS_VAR_TYPE-74] - _ = x[TRANS_VAR_VALUE-75] - _ = x[TRANS_TYPE_TYPE-76] - _ = x[TRANS_FILE_BODY-77] + _ = x[TRANS_FUNCLIT_HEAP_CAPTURE-21] + _ = x[TRANS_FUNCLIT_BODY-22] + _ = x[TRANS_FIELDTYPE_TYPE-23] + _ = x[TRANS_FIELDTYPE_TAG-24] + _ = x[TRANS_ARRAYTYPE_LEN-25] + _ = x[TRANS_ARRAYTYPE_ELT-26] + _ = x[TRANS_SLICETYPE_ELT-27] + _ = x[TRANS_INTERFACETYPE_METHOD-28] + _ = x[TRANS_CHANTYPE_VALUE-29] + _ = x[TRANS_FUNCTYPE_PARAM-30] + _ = x[TRANS_FUNCTYPE_RESULT-31] + _ = x[TRANS_MAPTYPE_KEY-32] + _ = x[TRANS_MAPTYPE_VALUE-33] + _ = x[TRANS_STRUCTTYPE_FIELD-34] + _ = x[TRANS_MAYBENATIVETYPE_TYPE-35] + _ = x[TRANS_ASSIGN_LHS-36] + _ = x[TRANS_ASSIGN_RHS-37] + _ = x[TRANS_BLOCK_BODY-38] + _ = x[TRANS_DECL_BODY-39] + _ = x[TRANS_DEFER_CALL-40] + _ = x[TRANS_EXPR_X-41] + _ = x[TRANS_FOR_INIT-42] + _ = x[TRANS_FOR_COND-43] + _ = x[TRANS_FOR_POST-44] + _ = x[TRANS_FOR_BODY-45] + _ = x[TRANS_GO_CALL-46] + _ = x[TRANS_IF_INIT-47] + _ = x[TRANS_IF_COND-48] + _ = x[TRANS_IF_BODY-49] + _ = x[TRANS_IF_ELSE-50] + _ = x[TRANS_IF_CASE_BODY-51] + _ = x[TRANS_INCDEC_X-52] + _ = x[TRANS_RANGE_X-53] + _ = x[TRANS_RANGE_KEY-54] + _ = x[TRANS_RANGE_VALUE-55] + _ = x[TRANS_RANGE_BODY-56] + _ = x[TRANS_RETURN_RESULT-57] + _ = x[TRANS_PANIC_EXCEPTION-58] + _ = x[TRANS_SELECT_CASE-59] + _ = x[TRANS_SELECTCASE_COMM-60] + _ = x[TRANS_SELECTCASE_BODY-61] + _ = x[TRANS_SEND_CHAN-62] + _ = x[TRANS_SEND_VALUE-63] + _ = x[TRANS_SWITCH_INIT-64] + _ = x[TRANS_SWITCH_X-65] + _ = x[TRANS_SWITCH_CASE-66] + _ = x[TRANS_SWITCHCASE_CASE-67] + _ = x[TRANS_SWITCHCASE_BODY-68] + _ = x[TRANS_FUNC_RECV-69] + _ = x[TRANS_FUNC_TYPE-70] + _ = x[TRANS_FUNC_BODY-71] + _ = x[TRANS_IMPORT_PATH-72] + _ = x[TRANS_CONST_TYPE-73] + _ = x[TRANS_CONST_VALUE-74] + _ = x[TRANS_VAR_NAME-75] + _ = x[TRANS_VAR_TYPE-76] + _ = x[TRANS_VAR_VALUE-77] + _ = x[TRANS_TYPE_TYPE-78] + _ = x[TRANS_FILE_BODY-79] } -const _TransField_name = "TRANS_ROOTTRANS_BINARY_LEFTTRANS_BINARY_RIGHTTRANS_CALL_FUNCTRANS_CALL_ARGTRANS_INDEX_XTRANS_INDEX_INDEXTRANS_SELECTOR_XTRANS_SLICE_XTRANS_SLICE_LOWTRANS_SLICE_HIGHTRANS_SLICE_MAXTRANS_STAR_XTRANS_REF_XTRANS_TYPEASSERT_XTRANS_TYPEASSERT_TYPETRANS_UNARY_XTRANS_COMPOSITE_TYPETRANS_COMPOSITE_KEYTRANS_COMPOSITE_VALUETRANS_FUNCLIT_TYPETRANS_FUNCLIT_BODYTRANS_FIELDTYPE_TYPETRANS_FIELDTYPE_TAGTRANS_ARRAYTYPE_LENTRANS_ARRAYTYPE_ELTTRANS_SLICETYPE_ELTTRANS_INTERFACETYPE_METHODTRANS_CHANTYPE_VALUETRANS_FUNCTYPE_PARAMTRANS_FUNCTYPE_RESULTTRANS_MAPTYPE_KEYTRANS_MAPTYPE_VALUETRANS_STRUCTTYPE_FIELDTRANS_MAYBENATIVETYPE_TYPETRANS_ASSIGN_LHSTRANS_ASSIGN_RHSTRANS_BLOCK_BODYTRANS_DECL_BODYTRANS_DEFER_CALLTRANS_EXPR_XTRANS_FOR_INITTRANS_FOR_CONDTRANS_FOR_POSTTRANS_FOR_BODYTRANS_GO_CALLTRANS_IF_INITTRANS_IF_CONDTRANS_IF_BODYTRANS_IF_ELSETRANS_IF_CASE_BODYTRANS_INCDEC_XTRANS_RANGE_XTRANS_RANGE_KEYTRANS_RANGE_VALUETRANS_RANGE_BODYTRANS_RETURN_RESULTTRANS_PANIC_EXCEPTIONTRANS_SELECT_CASETRANS_SELECTCASE_COMMTRANS_SELECTCASE_BODYTRANS_SEND_CHANTRANS_SEND_VALUETRANS_SWITCH_INITTRANS_SWITCH_XTRANS_SWITCH_CASETRANS_SWITCHCASE_CASETRANS_SWITCHCASE_BODYTRANS_FUNC_RECVTRANS_FUNC_TYPETRANS_FUNC_BODYTRANS_IMPORT_PATHTRANS_CONST_TYPETRANS_CONST_VALUETRANS_VAR_TYPETRANS_VAR_VALUETRANS_TYPE_TYPETRANS_FILE_BODY" +const _TransField_name = "TRANS_ROOTTRANS_BINARY_LEFTTRANS_BINARY_RIGHTTRANS_CALL_FUNCTRANS_CALL_ARGTRANS_INDEX_XTRANS_INDEX_INDEXTRANS_SELECTOR_XTRANS_SLICE_XTRANS_SLICE_LOWTRANS_SLICE_HIGHTRANS_SLICE_MAXTRANS_STAR_XTRANS_REF_XTRANS_TYPEASSERT_XTRANS_TYPEASSERT_TYPETRANS_UNARY_XTRANS_COMPOSITE_TYPETRANS_COMPOSITE_KEYTRANS_COMPOSITE_VALUETRANS_FUNCLIT_TYPETRANS_FUNCLIT_HEAP_CAPTURETRANS_FUNCLIT_BODYTRANS_FIELDTYPE_TYPETRANS_FIELDTYPE_TAGTRANS_ARRAYTYPE_LENTRANS_ARRAYTYPE_ELTTRANS_SLICETYPE_ELTTRANS_INTERFACETYPE_METHODTRANS_CHANTYPE_VALUETRANS_FUNCTYPE_PARAMTRANS_FUNCTYPE_RESULTTRANS_MAPTYPE_KEYTRANS_MAPTYPE_VALUETRANS_STRUCTTYPE_FIELDTRANS_MAYBENATIVETYPE_TYPETRANS_ASSIGN_LHSTRANS_ASSIGN_RHSTRANS_BLOCK_BODYTRANS_DECL_BODYTRANS_DEFER_CALLTRANS_EXPR_XTRANS_FOR_INITTRANS_FOR_CONDTRANS_FOR_POSTTRANS_FOR_BODYTRANS_GO_CALLTRANS_IF_INITTRANS_IF_CONDTRANS_IF_BODYTRANS_IF_ELSETRANS_IF_CASE_BODYTRANS_INCDEC_XTRANS_RANGE_XTRANS_RANGE_KEYTRANS_RANGE_VALUETRANS_RANGE_BODYTRANS_RETURN_RESULTTRANS_PANIC_EXCEPTIONTRANS_SELECT_CASETRANS_SELECTCASE_COMMTRANS_SELECTCASE_BODYTRANS_SEND_CHANTRANS_SEND_VALUETRANS_SWITCH_INITTRANS_SWITCH_XTRANS_SWITCH_CASETRANS_SWITCHCASE_CASETRANS_SWITCHCASE_BODYTRANS_FUNC_RECVTRANS_FUNC_TYPETRANS_FUNC_BODYTRANS_IMPORT_PATHTRANS_CONST_TYPETRANS_CONST_VALUETRANS_VAR_NAMETRANS_VAR_TYPETRANS_VAR_VALUETRANS_TYPE_TYPETRANS_FILE_BODY" -var _TransField_index = [...]uint16{0, 10, 27, 45, 60, 74, 87, 104, 120, 133, 148, 164, 179, 191, 202, 220, 241, 254, 274, 293, 314, 332, 350, 370, 389, 408, 427, 446, 472, 492, 512, 533, 550, 569, 591, 617, 633, 649, 665, 680, 696, 708, 722, 736, 750, 764, 777, 790, 803, 816, 829, 847, 861, 874, 889, 906, 922, 941, 962, 979, 1000, 1021, 1036, 1052, 1069, 1083, 1100, 1121, 1142, 1157, 1172, 1187, 1204, 1220, 1237, 1251, 1266, 1281, 1296} +var _TransField_index = [...]uint16{0, 10, 27, 45, 60, 74, 87, 104, 120, 133, 148, 164, 179, 191, 202, 220, 241, 254, 274, 293, 314, 332, 358, 376, 396, 415, 434, 453, 472, 498, 518, 538, 559, 576, 595, 617, 643, 659, 675, 691, 706, 722, 734, 748, 762, 776, 790, 803, 816, 829, 842, 855, 873, 887, 900, 915, 932, 948, 967, 988, 1005, 1026, 1047, 1062, 1078, 1095, 1109, 1126, 1147, 1168, 1183, 1198, 1213, 1230, 1246, 1263, 1277, 1291, 1306, 1321, 1336} func (i TransField) String() string { if i >= TransField(len(_TransField_index)-1) { diff --git a/gnovm/pkg/gnolang/type_check.go b/gnovm/pkg/gnolang/type_check.go index 2dec832fd0d..f86c44e7921 100644 --- a/gnovm/pkg/gnolang/type_check.go +++ b/gnovm/pkg/gnolang/type_check.go @@ -855,8 +855,8 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { if x.Op == ASSIGN { // check assignable for i, lx := range x.Lhs { + assertValidAssignLhs(store, last, lx) if !isBlankIdentifier(lx) { - assertValidAssignLhs(store, last, lx) lxt := evalStaticTypeOf(store, last, lx) assertAssignableTo(cft.Results[i].Type, lxt, false) } @@ -868,15 +868,16 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { panic("should not happen") } if x.Op == ASSIGN { - // check assignable to first value + // check first value + assertValidAssignLhs(store, last, x.Lhs[0]) if !isBlankIdentifier(x.Lhs[0]) { // see composite3.gno - assertValidAssignLhs(store, last, x.Lhs[0]) dt := evalStaticTypeOf(store, last, x.Lhs[0]) ift := evalStaticTypeOf(store, last, cx) assertAssignableTo(ift, dt, false) } + // check second value + assertValidAssignLhs(store, last, x.Lhs[1]) if !isBlankIdentifier(x.Lhs[1]) { // see composite3.gno - assertValidAssignLhs(store, last, x.Lhs[1]) dt := evalStaticTypeOf(store, last, x.Lhs[1]) if dt.Kind() != BoolKind { // typed, not bool panic(fmt.Sprintf("want bool type got %v", dt)) @@ -889,8 +890,8 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { panic("should not happen") } if x.Op == ASSIGN { + assertValidAssignLhs(store, last, x.Lhs[0]) if !isBlankIdentifier(x.Lhs[0]) { - assertValidAssignLhs(store, last, x.Lhs[0]) lt := evalStaticTypeOf(store, last, x.Lhs[0]) if _, ok := cx.X.(*NameExpr); ok { rt := evalStaticTypeOf(store, last, cx.X) @@ -906,8 +907,9 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { } } } + + assertValidAssignLhs(store, last, x.Lhs[1]) if !isBlankIdentifier(x.Lhs[1]) { - assertValidAssignLhs(store, last, x.Lhs[1]) dt := evalStaticTypeOf(store, last, x.Lhs[1]) if dt != nil && dt.Kind() != BoolKind { // typed, not bool panic(fmt.Sprintf("want bool type got %v", dt)) @@ -973,7 +975,17 @@ func (x *AssignStmt) AssertCompatible(store Store, last BlockNode) { func assertValidAssignLhs(store Store, last BlockNode, lx Expr) { shouldPanic := true switch clx := lx.(type) { - case *NameExpr, *StarExpr, *SelectorExpr: + case *NameExpr: + if clx.Name == blankIdentifier { + shouldPanic = false + } else if clx.Path.Type == VPUverse { + panic(fmt.Sprintf("cannot assign to uverse %v", clx.Name)) + } else if last.GetIsConst(store, clx.Name) { + panic(fmt.Sprintf("cannot assign to const %v", clx.Name)) + } else { + shouldPanic = false + } + case *StarExpr, *SelectorExpr: shouldPanic = false case *IndexExpr: xt := evalStaticTypeOf(store, last, clx.X) @@ -1017,7 +1029,7 @@ func isComparison(op Word) bool { // it returns true, indicating a swap is needed. func shouldSwapOnSpecificity(t1, t2 Type) bool { // check nil - if t1 == nil { // see test file 0f46 + if t1 == nil { return false // also with both nil } else if t2 == nil { return true @@ -1056,7 +1068,7 @@ func shouldSwapOnSpecificity(t1, t2 Type) bool { func isBlankIdentifier(x Expr) bool { if nx, ok := x.(*NameExpr); ok { - return nx.Name == "_" + return nx.Name == blankIdentifier } return false } diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go index b43f623ea99..eedb71ffa73 100644 --- a/gnovm/pkg/gnolang/types.go +++ b/gnovm/pkg/gnolang/types.go @@ -69,6 +69,7 @@ func (*PackageType) assertType() {} func (*ChanType) assertType() {} func (*NativeType) assertType() {} func (blockType) assertType() {} +func (heapItemType) assertType() {} func (*tupleType) assertType() {} func (RefType) assertType() {} func (MaybeNativeType) assertType() {} @@ -1964,6 +1965,35 @@ func (bt blockType) IsNamed() bool { panic("blockType has no property called named") } +// ---------------------------------------- +// heapItemType + +type heapItemType struct{} // no data + +func (bt heapItemType) Kind() Kind { + return HeapItemKind +} + +func (bt heapItemType) TypeID() TypeID { + return typeid("heapitem") +} + +func (bt heapItemType) String() string { + return "heapitem" +} + +func (bt heapItemType) Elem() Type { + panic("heapItemType has no elem type") +} + +func (bt heapItemType) GetPkgPath() string { + panic("heapItemType has no package path") +} + +func (bt heapItemType) IsNamed() bool { + panic("heapItemType has no property called named") +} + // ---------------------------------------- // tupleType @@ -2118,9 +2148,10 @@ const ( MapKind TypeKind // not in go. // UnsafePointerKind - BlockKind // not in go. - TupleKind // not in go. - RefTypeKind // not in go. + BlockKind // not in go. + HeapItemKind // not in go. + TupleKind // not in go. + RefTypeKind // not in go. ) // This is generally slower than switching on baseOf(t). @@ -2193,6 +2224,8 @@ func KindOf(t Type) Kind { return t.Kind() case blockType: return BlockKind + case heapItemType: + return HeapItemKind case *tupleType: return TupleKind case RefType: diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 2761d3f574b..68c2967811f 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -534,12 +534,13 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { // makes construction TypedValue{T:*FuncType{},V:*FuncValue{}} // faster. type FuncValue struct { - Type Type // includes unbound receiver(s) - IsMethod bool // is an (unbound) method - Source BlockNode // for block mem allocation - Name Name // name of function/method - Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) - FileName Name // file name where declared + Type Type // includes unbound receiver(s) + IsMethod bool // is an (unbound) method + Source BlockNode // for block mem allocation + Name Name // name of function/method + Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) + Captures []TypedValue `json:",omitempty"` // HeapItemValues captured from closure. + FileName Name // file name where declared PkgPath string NativePkg string // for native bindings through NativeStore NativeName Name // not redundant with Name; this cannot be changed in userspace @@ -1645,10 +1646,10 @@ func (tv *TypedValue) Assign(alloc *Allocator, tv2 TypedValue, cu bool) { // or binary operations. When a pointer is to be // allocated, *Allocator.AllocatePointer() is called separately, // as in OpRef. -func (tv *TypedValue) GetPointerTo(alloc *Allocator, store Store, path ValuePath) PointerValue { +func (tv *TypedValue) GetPointerToFromTV(alloc *Allocator, store Store, path ValuePath) PointerValue { if debug { if tv.IsUndefined() { - panic("GetPointerTo() on undefined value") + panic("GetPointerToFromTV() on undefined value") } } @@ -1867,7 +1868,7 @@ func (tv *TypedValue) GetPointerTo(alloc *Allocator, store Store, path ValuePath } bv := *dtv for i, path := range tr { - ptr := bv.GetPointerTo(alloc, store, path) + ptr := bv.GetPointerToFromTV(alloc, store, path) if i == len(tr)-1 { return ptr // done } @@ -2436,6 +2437,70 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { return b.GetPointerToInt(store, int(path.Index)) } +// Convenience +func (b *Block) GetPointerToMaybeHeapUse(store Store, nx *NameExpr) PointerValue { + switch nx.Type { + case NameExprTypeNormal: + return b.GetPointerTo(store, nx.Path) + case NameExprTypeHeapUse: + return b.GetPointerToHeapUse(store, nx.Path) + case NameExprTypeHeapClosure: + panic("should not happen with type heap closure") + default: + panic("unexpected NameExpr type for GetPointerToMaybeHeapUse") + } +} + +// Convenience +func (b *Block) GetPointerToMaybeHeapDefine(store Store, nx *NameExpr) PointerValue { + switch nx.Type { + case NameExprTypeNormal: + return b.GetPointerTo(store, nx.Path) + case NameExprTypeDefine: + return b.GetPointerTo(store, nx.Path) + case NameExprTypeHeapDefine: + return b.GetPointerToHeapDefine(store, nx.Path) + default: + panic("unexpected NameExpr type for GetPointerToMaybeHeapDefine") + } +} + +// First defines a new HeapItemValue. +// This gets called from NameExprTypeHeapDefine name expressions. +func (b *Block) GetPointerToHeapDefine(store Store, path ValuePath) PointerValue { + ptr := b.GetPointerTo(store, path) + hiv := &HeapItemValue{} + // point to a heapItem + *ptr.TV = TypedValue{ + T: heapItemType{}, + V: hiv, + } + + return PointerValue{ + TV: &hiv.Value, + Base: hiv, + Index: 0, + } +} + +// Assumes a HeapItemValue, and gets inner pointer. +// This gets called from NameExprTypeHeapUse name expressions. +func (b *Block) GetPointerToHeapUse(store Store, path ValuePath) PointerValue { + ptr := b.GetPointerTo(store, path) + if _, ok := ptr.TV.T.(heapItemType); !ok { + panic("should not happen, should be heapItemType") + } + if _, ok := ptr.TV.V.(*HeapItemValue); !ok { + panic("should not happen, should be HeapItemValue") + } + + return PointerValue{ + TV: &ptr.TV.V.(*HeapItemValue).Value, + Base: ptr.TV.V, + Index: 0, + } +} + // Result is used has lhs for any assignments to "_". func (b *Block) GetBlankRef() *TypedValue { return &b.Blank diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 1be2a0516f9..f45beffe648 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -110,7 +110,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { opt(&f) } - directives, pkgPath, resWanted, errWanted, rops, stacktraceWanted, maxAlloc, send := wantedFromComment(path) + directives, pkgPath, resWanted, errWanted, rops, stacktraceWanted, maxAlloc, send, preWanted := wantedFromComment(path) if pkgPath == "" { pkgPath = "main" } @@ -377,6 +377,28 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { } } } + case "Preprocessed": + // check preprocessed AST. + pn := store.GetBlockNode(gno.PackageNodeLocation(pkgPath)) + pre := pn.(*gno.PackageNode).FileSet.Files[0].String() + if pre != preWanted { + if f.syncWanted { + // write error to file + replaceWantedInPlace(path, "Preprocessed", pre) + } else { + // panic so tests immediately fail (for now). + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(preWanted), + B: difflib.SplitLines(pre), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff)) + } + } case "Stacktrace": if stacktraceWanted != "" { var stacktrace string @@ -388,22 +410,27 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { stacktrace = m.Stacktrace().String() } - if !strings.Contains(stacktrace, stacktraceWanted) { - diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(stacktraceWanted), - B: difflib.SplitLines(stacktrace), - FromFile: "Expected", - FromDate: "", - ToFile: "Actual", - ToDate: "", - Context: 1, - }) - panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff)) + if f.syncWanted { + // write stacktrace to file + replaceWantedInPlace(path, "Stacktrace", stacktrace) + } else { + if !strings.Contains(stacktrace, stacktraceWanted) { + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(stacktraceWanted), + B: difflib.SplitLines(stacktrace), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + panic(fmt.Sprintf("fail on %s: diff:\n%s\n", path, diff)) + } } } checkMachineIsEmpty = false default: - checkMachineIsEmpty = false + return nil } } } @@ -421,7 +448,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { return nil } -func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops, stacktrace string, maxAlloc int64, send std.Coins) { +func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops, stacktrace string, maxAlloc int64, send std.Coins, pre string) { fset := token.NewFileSet() f, err2 := parser.ParseFile(fset, p, nil, parser.ParseComments) if err2 != nil { @@ -463,6 +490,10 @@ func wantedFromComment(p string) (directives []string, pkgPath, res, err, rops, rops = strings.TrimPrefix(text, "Realm:\n") rops = strings.TrimSpace(rops) directives = append(directives, "Realm") + } else if strings.HasPrefix(text, "Preprocessed:\n") { + pre = strings.TrimPrefix(text, "Preprocessed:\n") + pre = strings.TrimSpace(pre) + directives = append(directives, "Preprocessed") } else if strings.HasPrefix(text, "Stacktrace:\n") { stacktrace = strings.TrimPrefix(text, "Stacktrace:\n") stacktrace = strings.TrimSpace(stacktrace) diff --git a/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno b/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno index 5bdd878c146..d61170334d7 100644 --- a/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno +++ b/gnovm/tests/files/assign_unnamed_type/more/realm_compositelit_filetest.gno @@ -1,10 +1,6 @@ // PKGPATH: gno.land/r/test package test -import ( - "fmt" -) - type ( word uint nat []word @@ -226,7 +222,7 @@ func main() { // "Location": { // "Column": "1", // "File": "main.gno", -// "Line": "20", +// "Line": "16", // "PkgPath": "gno.land/r/test" // } // }, diff --git a/gnovm/tests/files/heap_alloc_defer.gno b/gnovm/tests/files/heap_alloc_defer.gno new file mode 100644 index 00000000000..788d9326695 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_defer.gno @@ -0,0 +1,37 @@ +package main + +type Foo struct { + num int + f func() +} + +func main() { + s := []Foo{ + { + num: 1, + f: func() { println("hello") }, + }, + { + num: 2, + f: func() { println("hola") }, + }, + } + + // tt is heap defined every iteration, + // different with for loopvar spec. + for _, tt := range s { + f := func() { + println(tt.num) + } + f() + defer func() { + tt.f() + }() + } +} + +// Output: +// 1 +// 2 +// hola +// hola diff --git a/gnovm/tests/files/heap_alloc_defer2.gno b/gnovm/tests/files/heap_alloc_defer2.gno new file mode 100644 index 00000000000..dc865b1a430 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_defer2.gno @@ -0,0 +1,28 @@ +package main + +type rand struct{} + +func (r *rand) Seed() { + println("seed...") +} + +func fromSeed() *rand { + return &rand{} +} + +func genResult(s0 string, x int) (int, bool) { + z := 0 + println(z) + r := fromSeed() + defer func() { r.Seed() }() + + return -1, true +} + +func main() { + genResult("hey", 0) +} + +// Output: +// 0 +// seed... diff --git a/gnovm/tests/files/heap_alloc_forloop1.gno b/gnovm/tests/files/heap_alloc_forloop1.gno new file mode 100644 index 00000000000..c166bf8e167 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop1.gno @@ -0,0 +1,31 @@ +package main + +import "fmt" + +var s1 []*int + +func forLoopRef() { + defer func() { + for i, e := range s1 { + fmt.Printf("s1[%d] is: %d\n", i, *e) + } + }() + + for i := 0; i < 3; i++ { + s1 = append(s1, &i) + } +} + +func main() { + forLoopRef() +} + +// go 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(i<~VPBlock(1,0)>)) } }; func main() { forLoopRef() } } + +// Output: +// s1[0] is: 3 +// s1[1] is: 3 +// s1[2] is: 3 diff --git a/gnovm/tests/files/heap_alloc_forloop1a.gno b/gnovm/tests/files/heap_alloc_forloop1a.gno new file mode 100644 index 00000000000..6d0895902bd --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop1a.gno @@ -0,0 +1,39 @@ +package main + +import "fmt" + +type Int int + +var s1 []*Int + +func inc2(j *Int) { + *j = *j + 2 // Just as an example, increment j by 2. +} + +func forLoopRef() { + defer func() { + for i, e := range s1 { + fmt.Printf("s1[%d] is: %d\n", i, *e) + } + }() + + for i := Int(0); i < 10; inc2(&i) { + s1 = append(s1, &i) + } +} + +func main() { + forLoopRef() +} + +// go 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; import fmt fmt; type Int (const-type main.Int); var s1 []*(Int); func inc2(j *(Int)) { *(j) = *(j) + (const (2 main.Int)) }; func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 main.Int)); i<~VPBlock(1,0)> < (const (10 main.Int)); inc2(&(i<~VPBlock(1,0)>)) { s1 = (const (append func(x []*main.Int,args ...*main.Int)(res []*main.Int)))(s1, &(i<~VPBlock(1,0)>)) } }; func main() { forLoopRef() } } + +// Output: +// s1[0] is: 10 +// s1[1] is: 10 +// s1[2] is: 10 +// s1[3] is: 10 +// s1[4] is: 10 diff --git a/gnovm/tests/files/heap_alloc_forloop1b.gno b/gnovm/tests/files/heap_alloc_forloop1b.gno new file mode 100644 index 00000000000..35b167e4168 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop1b.gno @@ -0,0 +1,37 @@ +package main + +import "fmt" + +var s1 []*int + +func forLoopRef() { + defer func() { + for i, e := range s1 { + fmt.Printf("s1[%d] is: %d\n", i, *e) + } + }() + + for i := 0; i < 3; i++ { + r := i + r, ok := 0, true + println(ok, r) + s1 = append(s1, &i) + } +} + +func main() { + forLoopRef() +} + +// go 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { r := i<~VPBlock(1,0)>; r, ok := (const (0 int)), (const (true bool)); (const (println func(xs ...interface{})()))(ok, r); s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(i<~VPBlock(1,0)>)) } }; func main() { forLoopRef() } } + +// Output: +// true 0 +// true 0 +// true 0 +// s1[0] is: 3 +// s1[1] is: 3 +// s1[2] is: 3 diff --git a/gnovm/tests/files/heap_alloc_forloop2.gno b/gnovm/tests/files/heap_alloc_forloop2.gno new file mode 100644 index 00000000000..aa7f6e44dd3 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop2.gno @@ -0,0 +1,33 @@ +package main + +import "fmt" + +var s1 []*int + +func forLoopRef() { + defer func() { + for i, e := range s1 { + fmt.Printf("s1[%d] is: %d\n", i, *e) + } + }() + + for i := 0; i < 3; i++ { + z := i + 1 + s1 = append(s1, &z) + } +} + +func main() { + forLoopRef() +} + +// This does make 'z' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of z and z<~...>. + +// Preprocessed: +// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i < (const (3 int)); i++ { z := i + (const (1 int)); s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(z<~VPBlock(1,1)>)) } }; func main() { forLoopRef() } } + +// Output: +// s1[0] is: 1 +// s1[1] is: 2 +// s1[2] is: 3 diff --git a/gnovm/tests/files/heap_alloc_forloop2a.gno b/gnovm/tests/files/heap_alloc_forloop2a.gno new file mode 100644 index 00000000000..be4b089ccad --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop2a.gno @@ -0,0 +1,34 @@ +package main + +import "fmt" + +var s1 []*int + +func forLoopRef() { + defer func() { + for i, e := range s1 { + fmt.Printf("s1[%d] is: %d\n", i, *e) + } + }() + + for i := 0; i < 3; i++ { + z := i + s1 = append(s1, &z) + z++ + } +} + +func main() { + forLoopRef() +} + +// This does make 'z' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of z and z<~...>. + +// Preprocessed: +// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); for i := (const (0 int)); i < (const (3 int)); i++ { z := i; s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(z<~VPBlock(1,1)>)); z<~VPBlock(1,1)>++ } }; func main() { forLoopRef() } } + +// Output: +// s1[0] is: 1 +// s1[1] is: 2 +// s1[2] is: 3 diff --git a/gnovm/tests/files/heap_alloc_forloop3.gno b/gnovm/tests/files/heap_alloc_forloop3.gno new file mode 100644 index 00000000000..91c9b627120 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop3.gno @@ -0,0 +1,33 @@ +package main + +type f func() + +var fs []f + +func forLoopClosure() { + defer func() { + for _, f := range fs { + f() + } + }() + + for i := 0; i < 3; i++ { + z := i + fs = append(fs, func() { println(z) }) + } +} + +func main() { + forLoopClosure() +} + +// This does make 'z' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of z and z<()~...>. + +// Preprocessed: +// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { z := i; fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } } + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop3a.gno b/gnovm/tests/files/heap_alloc_forloop3a.gno new file mode 100644 index 00000000000..fd361e7134e --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop3a.gno @@ -0,0 +1,38 @@ +package main + +type f func() + +var fs []f + +func forLoopClosure() { + defer func() { + for _, f := range fs { + f() + } + }() + + for i := 0; i < 3; i++ { + x := i + println(x) + z := i + fs = append(fs, func() { println(z) }) + } +} + +func main() { + forLoopClosure() +} + +// This does make 'z' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of z and z<()~...>. + +// Preprocessed: +// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { x := i; (const (println func(xs ...interface{})()))(x); z := i; fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } } + +// Output: +// 0 +// 1 +// 2 +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop4.gno b/gnovm/tests/files/heap_alloc_forloop4.gno new file mode 100644 index 00000000000..3cddb1a60fe --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop4.gno @@ -0,0 +1,31 @@ +package main + +type f func() + +var fs []f + +func forLoopClosure() { + defer func() { + for _, f := range fs { + f() + } + }() + + for i := 0; i < 3; i++ { + fs = append(fs, func() { println(i) }) + } +} + +func main() { + forLoopClosure() +} + +// go 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ (const (println func(xs ...interface{})()))(i<~VPBlock(1,0)>) }>)) } }; func main() { forLoopClosure() } } + +// Output: +// 3 +// 3 +// 3 diff --git a/gnovm/tests/files/heap_alloc_forloop5.gno b/gnovm/tests/files/heap_alloc_forloop5.gno new file mode 100644 index 00000000000..4f563ec866b --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop5.gno @@ -0,0 +1,32 @@ +package main + +type f func() + +var fs []f + +func forLoopClosure() { + defer func() { + for _, f := range fs { + f() + } + }() + + for i := 0; i < 3; i++ { + fs = append(fs, func() { + z := i + println(z) + }) + } +} + +func main() { + forLoopClosure() +} + +// Preprocessed: +// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ z := i<~VPBlock(1,1)>; (const (println func(xs ...interface{})()))(z) }>)) } }; func main() { forLoopClosure() } } + +// Output: +// 3 +// 3 +// 3 diff --git a/gnovm/tests/files/heap_alloc_forloop5a.gno b/gnovm/tests/files/heap_alloc_forloop5a.gno new file mode 100644 index 00000000000..039be2b86a8 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop5a.gno @@ -0,0 +1,33 @@ +package main + +type f func() + +var fs []f + +func forLoopClosure() { + defer func() { + for _, f := range fs { + f() + } + }() + + for i := 0; i < 3; i++ { + x := i + fs = append(fs, func() { + z := x + println(z) + }) + } +} + +func main() { + forLoopClosure() +} + +// Preprocessed: +// file{ package main; type f (const-type main.f); var fs []f; func forLoopClosure() { defer func func(){ for _, f := range fs { f() } }(); for i := (const (0 int)); i < (const (3 int)); i++ { x := i; fs = (const (append func(x []main.f,args ...main.f)(res []main.f)))(fs, (const-type main.f)(func func(){ z := x<~VPBlock(1,1)>; (const (println func(xs ...interface{})()))(z) }>)) } }; func main() { forLoopClosure() } } + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop6.gno b/gnovm/tests/files/heap_alloc_forloop6.gno new file mode 100644 index 00000000000..6cfa8a65fc8 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6.gno @@ -0,0 +1,25 @@ +package main + +func main() { + var fns []func() int + + for i := 0; i < 3; i++ { + z := i + f := func() int { + return z + } + fns = append(fns, f) + } + + for _, fn := range fns { + println(fn()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (3 int)); i++ { z := i; f := func func() (const-type int){ return z<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop6a.gno b/gnovm/tests/files/heap_alloc_forloop6a.gno new file mode 100644 index 00000000000..6365fcd8c62 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6a.gno @@ -0,0 +1,26 @@ +package main + +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 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (5 int)); i<~VPBlock(1,0)>++ { f := func func() (const-type int){ return i<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 5 +// 5 +// 5 +// 5 +// 5 diff --git a/gnovm/tests/files/heap_alloc_forloop6b.gno b/gnovm/tests/files/heap_alloc_forloop6b.gno new file mode 100644 index 00000000000..73d9e5cd6a7 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6b.gno @@ -0,0 +1,25 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; y := (const (0 int)); f := func func() (const-type int){ x<~VPBlock(1,1)> += y<~VPBlock(1,2)>; x<~VPBlock(1,1)> += (const (1 int)); return x<~VPBlock(1,1)> }, y<()~VPBlock(1,2)>>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop6c.gno b/gnovm/tests/files/heap_alloc_forloop6c.gno new file mode 100644 index 00000000000..f8d2d410f6c --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6c.gno @@ -0,0 +1,23 @@ +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()) + } +} + +// go 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (2 int)); i<~VPBlock(1,0)>++ { f := func func() (const-type int){ return i<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 2 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop6f.gno b/gnovm/tests/files/heap_alloc_forloop6f.gno new file mode 100644 index 00000000000..fcc2cdfdcc1 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6f.gno @@ -0,0 +1,26 @@ +package main + +func main() { + var fns []func() int + for i := 0; i < 5; i++ { + var x int + f := func() int { + return x + } + x = i + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (5 int)); i++ { var x (const-type int); f := func func() (const-type int){ return x<~VPBlock(1,1)> }>; x<~VPBlock(1,1)> = i; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/heap_alloc_forloop6g.gno b/gnovm/tests/files/heap_alloc_forloop6g.gno new file mode 100644 index 00000000000..4ff7856c97c --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6g.gno @@ -0,0 +1,27 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (5 int)); i++ { x := i; { f := func func() (const-type int){ return x<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) } }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/heap_alloc_forloop6h.gno b/gnovm/tests/files/heap_alloc_forloop6h.gno new file mode 100644 index 00000000000..75b84bebf91 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6h.gno @@ -0,0 +1,33 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; for j := (const (0 int)); j < (const (2 int)); j++ { y := j; f := func func() (const-type int){ return x<~VPBlock(1,1)> + y<~VPBlock(1,2)> }, y<()~VPBlock(1,1)>>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) } }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Go Output: +// 0 +// 1 +// 1 +// 2 + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop6h0.gno b/gnovm/tests/files/heap_alloc_forloop6h0.gno new file mode 100644 index 00000000000..0225bd62cf6 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6h0.gno @@ -0,0 +1,27 @@ +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 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (2 int)); i<~VPBlock(1,0)>++ { for j := (const (0 int)); j<~VPBlock(1,0)> < (const (2 int)); j<~VPBlock(1,0)>++ { f := func func() (const-type int){ return i<~VPBlock(1,1)> + j<~VPBlock(1,2)> }, j<()~VPBlock(1,0)>>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) } }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 4 +// 4 +// 4 +// 4 diff --git a/gnovm/tests/files/heap_alloc_forloop6i.gno b/gnovm/tests/files/heap_alloc_forloop6i.gno new file mode 100644 index 00000000000..28bb17cbf5f --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop6i.gno @@ -0,0 +1,34 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); var x (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x = i; for j := (const (0 int)); j < (const (2 int)); j++ { y := j; f := func func() (const-type int){ return x + y<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) } }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Go Output: +// 1 +// 2 +// 1 +// 2 + +// Output: +// 1 +// 2 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop7.gno b/gnovm/tests/files/heap_alloc_forloop7.gno new file mode 100644 index 00000000000..95fdf42045d --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop7.gno @@ -0,0 +1,28 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; f := func func() (const-type int){ if (const (true bool)) { if (const (true bool)) { return x<~VPBlock(3,1)> } }; return (const (0 int)) }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_forloop7a.gno b/gnovm/tests/files/heap_alloc_forloop7a.gno new file mode 100644 index 00000000000..59e83022f57 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop7a.gno @@ -0,0 +1,26 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; f := func func() (const-type int){ if (const (true bool)) { return x<~VPBlock(2,1)> }; return (const (0 int)) }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_forloop8.gno b/gnovm/tests/files/heap_alloc_forloop8.gno new file mode 100644 index 00000000000..7c86fdc5b90 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop8.gno @@ -0,0 +1,25 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; f := func func() (const-type int){ { return x<~VPBlock(2,1)> } }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_forloop8a.gno b/gnovm/tests/files/heap_alloc_forloop8a.gno new file mode 100644 index 00000000000..f0864fa07da --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop8a.gno @@ -0,0 +1,26 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; f := func func() (const-type int){ for i := (const (0 int)); i < (const (1 int)); i++ { x<~VPBlock(2,1)>++ }; return x<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop8b.gno b/gnovm/tests/files/heap_alloc_forloop8b.gno new file mode 100644 index 00000000000..1cd6bf13cc3 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop8b.gno @@ -0,0 +1,28 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; s := [](const-type int){(const (1 int)), (const (2 int))}; f := func func() (const-type int){ for _, v := range s<~VPBlock(2,1)> { x<~VPBlock(2,2)> += v }; return x<~VPBlock(1,2)> }, x<()~VPBlock(1,1)>>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 3 +// 4 diff --git a/gnovm/tests/files/heap_alloc_forloop8c.gno b/gnovm/tests/files/heap_alloc_forloop8c.gno new file mode 100644 index 00000000000..11aef1683b8 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop8c.gno @@ -0,0 +1,30 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); for i := (const (0 int)); i < (const (2 int)); i++ { x := i; y := (const (1 int)); f := func func() (const-type int){ switch y<~VPBlock(2,1)> { case (const (1 int)): x<~VPBlock(2,2)> += (const (1 int)); default: x<~VPBlock(2,2)> += (const (0 int)) }; return x<~VPBlock(1,2)> }, x<()~VPBlock(1,1)>>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_forloop9.gno b/gnovm/tests/files/heap_alloc_forloop9.gno new file mode 100644 index 00000000000..0f1d0b8b23c --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop9.gno @@ -0,0 +1,42 @@ +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 1.22 loop var is not supported for now. + +// Preprocessed: +// file{ package main; import fmt fmt; func main() { var fns []func(.arg_0 (const-type int)) (const-type int); var recursiveFunc func(.arg_0 (const-type int)) (const-type int); for i := (const (0 int)); i<~VPBlock(1,0)> < (const (3 int)); i<~VPBlock(1,0)>++ { recursiveFunc = func func(num (const-type int)) (const-type int){ x := i<~VPBlock(1,3)>; (const (println func(xs ...interface{})()))((const ("value of x: " string)), x); if num <= (const (0 int)) { return (const (1 int)) }; return num * recursiveFunc(num - (const (1 int))) }>; fns = (const (append func(x []func(.arg_0 int)( int),args ...func(.arg_0 int)( int))(res []func(.arg_0 int)( int))))(fns, recursiveFunc) }; for i, r := range fns { result := r(i); fmt.Printf((const ("Factorial of %d is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(result)) } } } + +// 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 diff --git a/gnovm/tests/files/heap_alloc_forloop9_1.gno b/gnovm/tests/files/heap_alloc_forloop9_1.gno new file mode 100644 index 00000000000..5e3b9af74f6 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop9_1.gno @@ -0,0 +1,25 @@ +package main + +func Search(n int, f func(int) bool) int { + f(1) + return 0 +} + +// TODO: identify this pattern, optimize. +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) + } +} + +// Preprocessed: +// file{ package main; func Search(n (const-type int), f func(.arg_0 (const-type int)) (const-type bool)) (const-type int) { f((const (1 int))); return (const (0 int)) }; func main() { for x := (const (0 int)); x<~VPBlock(1,0)> < (const (2 int)); x<~VPBlock(1,0)>++ { count := (const (0 int)); (const (println func(xs ...interface{})()))((const (" first: count: " string)), count<~VPBlock(1,1)>); Search((const (1 int)), func func(i (const-type int)) (const-type bool){ count<~VPBlock(1,2)>++; return (const-type bool)(i >= x<~VPBlock(1,3)>) }, x<()~VPBlock(1,0)>>); (const (println func(xs ...interface{})()))((const ("second: count: " string)), count<~VPBlock(1,1)>) } } } + +// Output: +// first: count: 0 +// second: count: 1 +// first: count: 0 +// second: count: 1 diff --git a/gnovm/tests/files/heap_alloc_forloop9_2.gno b/gnovm/tests/files/heap_alloc_forloop9_2.gno new file mode 100644 index 00000000000..6b1ebdbb7e9 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop9_2.gno @@ -0,0 +1,36 @@ +package main + +func main() { + var fns []func() int + + println("start for loop") + for i := 0; i < 2; i++ { + defer func() { + println("defer") + for _, fn := range fns { + println(fn()) + } + }() + + x := i + f := func() int { + return x + } + + fns = append(fns, f) + } + println("end for loop") +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); (const (println func(xs ...interface{})()))((const ("start for loop" string))); for i := (const (0 int)); i < (const (2 int)); i++ { defer func func(){ (const (println func(xs ...interface{})()))((const ("defer" string))); for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } }(); x := i; f := func func() (const-type int){ return x<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; (const (println func(xs ...interface{})()))((const ("end for loop" string))) } } + +// Output: +// start for loop +// end for loop +// defer +// 0 +// 1 +// defer +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_forloop9b.gno b/gnovm/tests/files/heap_alloc_forloop9b.gno new file mode 100644 index 00000000000..03e84fcd2b2 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_forloop9b.gno @@ -0,0 +1,28 @@ +package main + +func main() { + var y int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + for i := 0; i < 2; i++ { + for j := 0; j < 2; j++ { + x := y + f = append(f, func() { println(x) }) + y++ + } + } +} + +// Preprocessed: +// file{ package main; func main() { var y (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); for i := (const (0 int)); i < (const (2 int)); i++ { for j := (const (0 int)); j < (const (2 int)); j++ { x := y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++ } } } } + +// Output: +// 0 +// 1 +// 2 +// 3 diff --git a/gnovm/tests/files/heap_alloc_gotoloop0.gno b/gnovm/tests/files/heap_alloc_gotoloop0.gno new file mode 100644 index 00000000000..d13baedf7c4 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop0.gno @@ -0,0 +1,28 @@ +package main + +func main() { + var counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually an implicit for loop +LABEL_1: + if counter == 2 { + return + } + x := counter + f = append(f, func() { println(x) }) + counter++ + goto LABEL_1 +} + +// Preprocessed: +// file{ package main; func main() { var counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); if counter == (const (2 int)) { return }; x := counter; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); counter++; goto LABEL_1<0,3> } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop1.gno b/gnovm/tests/files/heap_alloc_gotoloop1.gno new file mode 100644 index 00000000000..ea26952e0a4 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop1.gno @@ -0,0 +1,30 @@ +package main + +func main() { + c := 0 +loop: + i := 1 + println(i) + c += 1 + if c < 10 { + goto loop + } +} + +// This does not make 'i' NameExprTypeHeapDefine, +// because it is not actually used in a &ref or closure context. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); i := (const (1 int)); (const (println func(xs ...interface{})()))(i); c += (const (1 int)); if c < (const (10 int)) { goto loop<1,1> } } } + +// Output: +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop2.gno b/gnovm/tests/files/heap_alloc_gotoloop2.gno new file mode 100644 index 00000000000..e7b2917a8ff --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop2.gno @@ -0,0 +1,37 @@ +package main + +func main() { + c := 0 + closures := []func(){} +loop: + i := c + closures = append(closures, func() { + println(i) + }) + c += 1 + if c < 10 { + goto loop + } + + for _, cl := range closures { + cl() + } +} + +// This does make 'i' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of i and i<()~...>. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); closures := []func(){}; i := c; closures = (const (append func(x []func()(),args ...func()())(res []func()())))(closures, func func(){ (const (println func(xs ...interface{})()))(i<~VPBlock(1,0)>) }>); c += (const (1 int)); if c < (const (10 int)) { goto loop<1,2> }; for _, cl := range closures { cl() } } } + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 diff --git a/gnovm/tests/files/heap_alloc_gotoloop3.gno b/gnovm/tests/files/heap_alloc_gotoloop3.gno new file mode 100644 index 00000000000..a248e8aa0bc --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop3.gno @@ -0,0 +1,34 @@ +package main + +func main() { + c := 0 + refs := []*int{} +loop: + i := c + refs = append(refs, &i) + c += 1 + if c < 10 { + goto loop + } + for _, ref := range refs { + println(*ref) + } +} + +// This does make 'i' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of i and i<~...>. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); refs := []*((const-type int)){}; i := c; refs = (const (append func(x []*int,args ...*int)(res []*int)))(refs, &(i<~VPBlock(1,2)>)); c += (const (1 int)); if c < (const (10 int)) { goto loop<1,2> }; for _, ref := range refs { (const (println func(xs ...interface{})()))(*(ref)) } } } + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 diff --git a/gnovm/tests/files/heap_alloc_gotoloop4.gno b/gnovm/tests/files/heap_alloc_gotoloop4.gno new file mode 100644 index 00000000000..062d832ac16 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop4.gno @@ -0,0 +1,34 @@ +package main + +func main() { + c := 0 + refs := []*int{} +loop: + var i int = c + refs = append(refs, &i) + c += 1 + if c < 10 { + goto loop + } + for _, ref := range refs { + println(*ref) + } +} + +// This does make 'i' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of i and i<~...>. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); refs := []*((const-type int)){}; var i (const-type int) = c; refs = (const (append func(x []*int,args ...*int)(res []*int)))(refs, &(i<~VPBlock(1,2)>)); c += (const (1 int)); if c < (const (10 int)) { goto loop<1,2> }; for _, ref := range refs { (const (println func(xs ...interface{})()))(*(ref)) } } } + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 diff --git a/gnovm/tests/files/heap_alloc_gotoloop5.gno b/gnovm/tests/files/heap_alloc_gotoloop5.gno new file mode 100644 index 00000000000..fb7a82228f1 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop5.gno @@ -0,0 +1,39 @@ +package main + +func main() { + c := 0 + refs := []*int{} +loop: + i, j := c, 2 + refs = append(refs, &i) + i += 1 + j += 1 + c += 1 + if c < 10 { + goto loop + } + for _, ref := range refs { + println(*ref) + } + if false { + println(j) // dummy usage + } +} + +// This does make 'i' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of i and i<~...>. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); refs := []*((const-type int)){}; i, j := c, (const (2 int)); refs = (const (append func(x []*int,args ...*int)(res []*int)))(refs, &(i<~VPBlock(1,2)>)); i<~VPBlock(1,2)> += (const (1 int)); j += (const (1 int)); c += (const (1 int)); if c < (const (10 int)) { goto loop<1,2> }; for _, ref := range refs { (const (println func(xs ...interface{})()))(*(ref)) }; if (const (false bool)) { (const (println func(xs ...interface{})()))(j) } } } + +// Output: +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 +// 10 diff --git a/gnovm/tests/files/heap_alloc_gotoloop6.gno b/gnovm/tests/files/heap_alloc_gotoloop6.gno new file mode 100644 index 00000000000..d2efbb85df2 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop6.gno @@ -0,0 +1,39 @@ +package main + +func main() { + c := 0 + refs := []*int{} +loop: + var i, j int = c, 2 + refs = append(refs, &i) + i += 1 + j += 1 + c += 1 + if c < 10 { + goto loop + } + for _, ref := range refs { + println(*ref) + } + if false { + println(j) // dummy usage + } +} + +// This does make 'i' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of i and i<~...>. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); refs := []*((const-type int)){}; var i, j (const-type int) = c, (const (2 int)); refs = (const (append func(x []*int,args ...*int)(res []*int)))(refs, &(i<~VPBlock(1,2)>)); i<~VPBlock(1,2)> += (const (1 int)); j += (const (1 int)); c += (const (1 int)); if c < (const (10 int)) { goto loop<1,2> }; for _, ref := range refs { (const (println func(xs ...interface{})()))(*(ref)) }; if (const (false bool)) { (const (println func(xs ...interface{})()))(j) } } } + +// Output: +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 +// 10 diff --git a/gnovm/tests/files/heap_alloc_gotoloop7.gno b/gnovm/tests/files/heap_alloc_gotoloop7.gno new file mode 100644 index 00000000000..457fdb74a01 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop7.gno @@ -0,0 +1,48 @@ +package main + +func main() { + c := 0 + refs := []*int{} +loop: + var i, j int = c, 2 + refs = append(refs, &i) + i += 1 + j += 1 + c += 1 + thing := func() { + i := 2 // new i + j := 3 // new j + println(i) + println(j) + } + if c < 10 { + goto loop + } + for _, ref := range refs { + println(*ref) + } + if false { + println(j) // dummy usage + } + if false { + thing() // dummy usage + } +} + +// This does make 'i' NameExprTypeHeapDefine. +// You can tell by the preprocess printout of i and i<~...>. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); refs := []*((const-type int)){}; var i, j (const-type int) = c, (const (2 int)); refs = (const (append func(x []*int,args ...*int)(res []*int)))(refs, &(i<~VPBlock(1,2)>)); i<~VPBlock(1,2)> += (const (1 int)); j += (const (1 int)); c += (const (1 int)); thing := func func(){ i := (const (2 int)); j := (const (3 int)); (const (println func(xs ...interface{})()))(i); (const (println func(xs ...interface{})()))(j) }; if c < (const (10 int)) { goto loop<1,2> }; for _, ref := range refs { (const (println func(xs ...interface{})()))(*(ref)) }; if (const (false bool)) { (const (println func(xs ...interface{})()))(j) }; if (const (false bool)) { thing() } } } + +// Output: +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 +// 8 +// 9 +// 10 diff --git a/gnovm/tests/files/heap_alloc_gotoloop8.gno b/gnovm/tests/files/heap_alloc_gotoloop8.gno new file mode 100644 index 00000000000..f3421048d41 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop8.gno @@ -0,0 +1,37 @@ +package main + +func main() { + c := 0 + closures := []func(){} + goto loop1 +loop1: // not a loop + i := 1 +loop2: + closures = append(closures, func() { + println(i) + }) + c += 1 + if c < 10 { + goto loop2 + } + for _, cl := range closures { + cl() + } +} + +// This one doesn't because the goto stmt doesn't go back far enough. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); closures := []func(){}; goto loop1<0,3>; i := (const (1 int)); closures = (const (append func(x []func()(),args ...func()())(res []func()())))(closures, func func(){ (const (println func(xs ...interface{})()))(i) }); c += (const (1 int)); if c < (const (10 int)) { goto loop2<1,4> }; for _, cl := range closures { cl() } } } + +// Output: +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9.gno b/gnovm/tests/files/heap_alloc_gotoloop9.gno new file mode 100644 index 00000000000..38204652216 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9.gno @@ -0,0 +1,35 @@ +package main + +func main() { + c := 0 + closures := []func(){} + i := 1 +loop2: + closures = append(closures, func() { + println(i) + }) + c += 1 + if c < 10 { + goto loop2 + } + for _, cl := range closures { + cl() + } +} + +// This one doesn't because the goto stmt doesn't go back far enough. + +// Preprocessed: +// file{ package main; func main() { c := (const (0 int)); closures := []func(){}; i := (const (1 int)); closures = (const (append func(x []func()(),args ...func()())(res []func()())))(closures, func func(){ (const (println func(xs ...interface{})()))(i) }); c += (const (1 int)); if c < (const (10 int)) { goto loop2<1,3> }; for _, cl := range closures { cl() } } } + +// Output: +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_10.gno b/gnovm/tests/files/heap_alloc_gotoloop9_10.gno new file mode 100644 index 00000000000..bb8a2bad3ef --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_10.gno @@ -0,0 +1,64 @@ +package main + +import "fmt" + +var s1 []*int +var s2 []*int + +// intersection loop +func main() { + defer func() { + for i, v := range s1 { + fmt.Printf("s1[%d] is %d\n", i, *v) + } + for i, v := range s2 { + fmt.Printf("s2[%d] is %d\n", i, *v) + } + }() + + // counter for loop + var c1, c2 int + +LOOP_1: + x := c1 + s1 = append(s1, &x) + println("loop_1", c1) + c1++ + +LOOP_2: + y := c2 + s2 = append(s2, &y) + println("loop_2", c2) + c2++ + + if c1 < 3 { + goto LOOP_1 + } + + if c2 < 6 { + goto LOOP_2 + } +} + +// Preprocessed: +// file{ package main; import fmt fmt; var s1 []*((const-type int)); var s2 []*((const-type int)); func main() { defer func func(){ for i, v := range s1 { fmt.Printf((const ("s1[%d] is %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(v))) }; for i, v := range s2 { fmt.Printf((const ("s2[%d] is %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(v))) } }(); var c1, c2 (const-type int); x := c1; s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(x<~VPBlock(1,2)>)); (const (println func(xs ...interface{})()))((const ("loop_1" string)), c1); c1++; y := c2; s2 = (const (append func(x []*int,args ...*int)(res []*int)))(s2, &(y<~VPBlock(1,3)>)); (const (println func(xs ...interface{})()))((const ("loop_2" string)), c2); c2++; if c1 < (const (3 int)) { goto LOOP_1<1,2> }; if c2 < (const (6 int)) { goto LOOP_2<1,6> } } } + +// Output: +// loop_1 0 +// loop_2 0 +// loop_1 1 +// loop_2 1 +// loop_1 2 +// loop_2 2 +// loop_2 3 +// loop_2 4 +// loop_2 5 +// s1[0] is 0 +// s1[1] is 1 +// s1[2] is 2 +// s2[0] is 0 +// s2[1] is 1 +// s2[2] is 2 +// s2[3] is 3 +// s2[4] is 4 +// s2[5] is 5 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_11.gno b/gnovm/tests/files/heap_alloc_gotoloop9_11.gno new file mode 100644 index 00000000000..839612704a8 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_11.gno @@ -0,0 +1,29 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + // this is actually an implicit for loop +LABEL_1: + if counter == 2 { + return + } + var _, x = 0, y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 +} + +// Preprocessed: +// file{ package main; func main() { var y, counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); if counter == (const (2 int)) { return }; var _, x = (const (0 int)), y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_1<0,3> } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_12.gno b/gnovm/tests/files/heap_alloc_gotoloop9_12.gno new file mode 100644 index 00000000000..4d024a58673 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_12.gno @@ -0,0 +1,56 @@ +package main + +import "fmt" + +func main() { + counter0 := 0 + counter1 := 0 + + y := 0 + + var fs []func() + + defer func() { + for _, ff := range fs { + ff() + } + }() + +LOOP_START: + if counter0 < 2 { + counter1 = 0 + fmt.Printf("Outer loop start: counter0=%d\n", counter0) + + NESTED_LOOP_START: + if counter1 < 2 { + fmt.Printf(" Nested loop: counter1=%d\n", counter1) + counter1++ + goto NESTED_LOOP_START + } + + x := y + fs = append(fs, func() { println(x) }) + + fmt.Println("Exiting nested loop") + counter0++ + y++ + goto LOOP_START + } else { + return + } +} + +// Preprocessed: +// file{ package main; import fmt fmt; func main() { counter0 := (const (0 int)); counter1 := (const (0 int)); y := (const (0 int)); var fs []func(); defer func func(){ for _, ff := range fs { ff() } }(); if counter0 < (const (2 int)) { counter1 = (const (0 int)); fmt.Printf((const ("Outer loop start: counter0=%d\n" string)), (const-type gonative{interface {}})(counter0)); if counter1 < (const (2 int)) { fmt.Printf((const (" Nested loop: counter1=%d\n" string)), (const-type gonative{interface {}})(counter1)); counter1++; goto NESTED_LOOP_START<1,2> }; x := y; fs = (const (append func(x []func()(),args ...func()())(res []func()())))(fs, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); fmt.Println((const ("Exiting nested loop" string))); counter0++; y++; goto LOOP_START<1,5> } else { return } } } + +// Output: +// Outer loop start: counter0=0 +// Nested loop: counter1=0 +// Nested loop: counter1=1 +// Exiting nested loop +// Outer loop start: counter0=1 +// Nested loop: counter1=0 +// Nested loop: counter1=1 +// Exiting nested loop +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_13.gno b/gnovm/tests/files/heap_alloc_gotoloop9_13.gno new file mode 100644 index 00000000000..7c7b6777223 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_13.gno @@ -0,0 +1,41 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + +LABEL_1: + x := y + if counter == 2 { + counter = 0 + goto LABEL_2 + } + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 + +LABEL_2: + if counter == 2 { + return + } + z := y + f = append(f, func() { println(z) }) + y++ + counter++ + goto LABEL_2 +} + +// Preprocessed: +// file{ package main; func main() { var y, counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); x := y; if counter == (const (2 int)) { counter = (const (0 int)); goto LABEL_2<1,9> }; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_1<0,3>; if counter == (const (2 int)) { return }; z := y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_2<0,9> } } + +// Output: +// 0 +// 1 +// 2 +// 3 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_14.gno b/gnovm/tests/files/heap_alloc_gotoloop9_14.gno new file mode 100644 index 00000000000..f92b31eb7de --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_14.gno @@ -0,0 +1,43 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + { + LABEL_1: + x := y + if counter == 2 { + counter = 0 + goto LABEL_2 + } + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 + } + +LABEL_2: + if counter == 2 { + return + } + z := y + f = append(f, func() { println(z) }) + y++ + counter++ + goto LABEL_2 +} + +// Preprocessed: +// file{ package main; func main() { var y, counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); { x := y; if counter == (const (2 int)) { counter = (const (0 int)); goto LABEL_2<2,4> }; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_1<0,0> }; if counter == (const (2 int)) { return }; z := y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_2<0,4> } } + +// Output: +// 0 +// 1 +// 2 +// 3 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_15.gno b/gnovm/tests/files/heap_alloc_gotoloop9_15.gno new file mode 100644 index 00000000000..d774af55eb6 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_15.gno @@ -0,0 +1,48 @@ +package main + +var y, counter int +var f []func() + +func main() { + defer func() { + for _, ff := range f { // XXX, why defer on this not work + ff() + } + }() +LABEL_1: + x := y + if counter == 2 { + counter = 0 + bar() + return + } + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 +} + +func bar() { + println("---bar---") +LABEL_2: + if counter == 2 { + println("---end---") + return + } + z := y + f = append(f, func() { println(z) }) + y++ + counter++ + goto LABEL_2 +} + +// Preprocessed: +// file{ package main; var y, counter (const-type int); var f []func(); func main() { defer func func(){ for _, ff := range f { ff() } }(); x := y; if counter == (const (2 int)) { counter = (const (0 int)); bar(); return }; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_1<0,1> }; func bar() { (const (println func(xs ...interface{})()))((const ("---bar---" string))); if counter == (const (2 int)) { (const (println func(xs ...interface{})()))((const ("---end---" string))); return }; z := y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_2<0,1> } } + +// Output: +// ---bar--- +// ---end--- +// 0 +// 1 +// 2 +// 3 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_15a.gno b/gnovm/tests/files/heap_alloc_gotoloop9_15a.gno new file mode 100644 index 00000000000..2d9de02ecc3 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_15a.gno @@ -0,0 +1,46 @@ +package main + +var y, counter int +var f []func() + +func main() { +LABEL_1: + x := y + if counter == 2 { + counter = 0 + bar() + for _, ff := range f { // XXX, why defer on this not work + ff() + } + return + } + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 +} + +func bar() { + println("---bar---") +LABEL_2: + if counter == 2 { + println("---end---") + return + } + z := y + f = append(f, func() { println(z) }) + y++ + counter++ + goto LABEL_2 +} + +// Preprocessed: +// file{ package main; var y, counter (const-type int); var f []func(); func main() { x := y; if counter == (const (2 int)) { counter = (const (0 int)); bar(); for _, ff := range f { ff() }; return }; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_1<0,0> }; func bar() { (const (println func(xs ...interface{})()))((const ("---bar---" string))); if counter == (const (2 int)) { (const (println func(xs ...interface{})()))((const ("---end---" string))); return }; z := y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(z<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_2<0,1> } } + +// Output: +// ---bar--- +// ---end--- +// 0 +// 1 +// 2 +// 3 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_16.gno b/gnovm/tests/files/heap_alloc_gotoloop9_16.gno new file mode 100644 index 00000000000..8971b04aee6 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_16.gno @@ -0,0 +1,58 @@ +package main + +import "fmt" + +func main() { + counter0 := 0 + counter1 := 0 + + y := 0 + + var fs []func() + + defer func() { + for _, ff := range fs { + ff() + } + }() + +LOOP_START: + if counter0 < 2 { + x := y + counter1 = 0 + fmt.Printf("Outer loop start: counter0=%d\n", counter0) + + NESTED_LOOP_START: + if counter1 < 2 { + fmt.Printf(" Nested loop: counter1=%d\n", counter1) + fs = append(fs, func() { println(x) }) + + counter1++ + goto NESTED_LOOP_START + } + + fmt.Println("Exiting nested loop") + counter0++ + y++ + goto LOOP_START + } else { + return + } +} + +// Preprocessed: +// file{ package main; import fmt fmt; func main() { counter0 := (const (0 int)); counter1 := (const (0 int)); y := (const (0 int)); var fs []func(); defer func func(){ for _, ff := range fs { ff() } }(); if counter0 < (const (2 int)) { x := y; counter1 = (const (0 int)); fmt.Printf((const ("Outer loop start: counter0=%d\n" string)), (const-type gonative{interface {}})(counter0)); if counter1 < (const (2 int)) { fmt.Printf((const (" Nested loop: counter1=%d\n" string)), (const-type gonative{interface {}})(counter1)); fs = (const (append func(x []func()(),args ...func()())(res []func()())))(fs, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); counter1++; goto NESTED_LOOP_START<1,3> }; fmt.Println((const ("Exiting nested loop" string))); counter0++; y++; goto LOOP_START<1,5> } else { return } } } + +// Output: +// Outer loop start: counter0=0 +// Nested loop: counter1=0 +// Nested loop: counter1=1 +// Exiting nested loop +// Outer loop start: counter0=1 +// Nested loop: counter1=0 +// Nested loop: counter1=1 +// Exiting nested loop +// 0 +// 0 +// 1 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_17.gno b/gnovm/tests/files/heap_alloc_gotoloop9_17.gno new file mode 100644 index 00000000000..9e5073c1bfe --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_17.gno @@ -0,0 +1,58 @@ +package main + +import "fmt" + +func main() { + counter0 := 0 + counter1 := 0 + + y := 0 + + var fs []func() + + defer func() { + for _, ff := range fs { + ff() + } + }() + +LOOP_START: + x := y + if counter0 < 2 { + counter1 = 0 + fmt.Printf("Outer loop start: counter0=%d\n", counter0) + + NESTED_LOOP_START: + if counter1 < 2 { + fmt.Printf(" Nested loop: counter1=%d\n", counter1) + fs = append(fs, func() { println(x) }) + + counter1++ + goto NESTED_LOOP_START + } + + fmt.Println("Exiting nested loop") + counter0++ + y++ + goto LOOP_START + } else { + return + } +} + +// Preprocessed: +// file{ package main; import fmt fmt; func main() { counter0 := (const (0 int)); counter1 := (const (0 int)); y := (const (0 int)); var fs []func(); defer func func(){ for _, ff := range fs { ff() } }(); x := y; if counter0 < (const (2 int)) { counter1 = (const (0 int)); fmt.Printf((const ("Outer loop start: counter0=%d\n" string)), (const-type gonative{interface {}})(counter0)); if counter1 < (const (2 int)) { fmt.Printf((const (" Nested loop: counter1=%d\n" string)), (const-type gonative{interface {}})(counter1)); fs = (const (append func(x []func()(),args ...func()())(res []func()())))(fs, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); counter1++; goto NESTED_LOOP_START<1,2> }; fmt.Println((const ("Exiting nested loop" string))); counter0++; y++; goto LOOP_START<1,5> } else { return } } } + +// Output: +// Outer loop start: counter0=0 +// Nested loop: counter1=0 +// Nested loop: counter1=1 +// Exiting nested loop +// Outer loop start: counter0=1 +// Nested loop: counter1=0 +// Nested loop: counter1=1 +// Exiting nested loop +// 0 +// 0 +// 1 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_18.gno b/gnovm/tests/files/heap_alloc_gotoloop9_18.gno new file mode 100644 index 00000000000..1578eb5897f --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_18.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + { + LABEL_1: + if counter == 2 { + return + } + x := y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 + } +} + +// Preprocessed: +// file{ package main; func main() { var y, counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); { if counter == (const (2 int)) { return }; x := y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_1<0,0> } } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_19.gno b/gnovm/tests/files/heap_alloc_gotoloop9_19.gno new file mode 100644 index 00000000000..fb1b995372e --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_19.gno @@ -0,0 +1,30 @@ +package main + +func main() { + var y, counter int + var f []func() func() int + defer func() { + for _, ff := range f { + println(ff()()) + } + }() + +LABEL_1: + if counter == 2 { + return + } + x := y + f = append(f, func() func() int { + return func() int { return x } + }) + y++ + counter++ + goto LABEL_1 +} + +// Preprocessed: +// file{ package main; func main() { var y, counter (const-type int); var f []func() func() (const-type int); defer func func(){ for _, ff := range f { (const (println func(xs ...interface{})()))(ff()()) } }(); if counter == (const (2 int)) { return }; x := y; f = (const (append func(x []func()( func()( int)),args ...func()( func()( int)))(res []func()( func()( int)))))(f, func func() func() (const-type int){ return func func() (const-type int){ return x<~VPBlock(2,1)> } }>); y++; counter++; goto LABEL_1<0,3> } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_20.gno b/gnovm/tests/files/heap_alloc_gotoloop9_20.gno new file mode 100644 index 00000000000..04896b425a5 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_20.gno @@ -0,0 +1,32 @@ +package main + +func main() { + var y, counter int + var f []func() (int, func() int) + defer func() { + for _, ff := range f { + n, f := ff() + println(n + f()) + } + }() + +LABEL_1: + if counter == 2 { + return + } + x := y + z := y + f = append(f, func() (int, func() int) { + return z, func() int { return x } + }) + y++ + counter++ + goto LABEL_1 +} + +// Preprocessed: +// file{ package main; func main() { var y, counter (const-type int); var f []func() (const-type int), func() (const-type int); defer func func(){ for _, ff := range f { n, f := ff(); (const (println func(xs ...interface{})()))(n + f()) } }(); if counter == (const (2 int)) { return }; x := y; z := y; f = (const (append func(x []func()( int, func()( int)),args ...func()( int, func()( int)))(res []func()( int, func()( int)))))(f, func func() (const-type int), func() (const-type int){ return z<~VPBlock(1,2)>, func func() (const-type int){ return x<~VPBlock(2,3)> } }, x<()~VPBlock(1,3)>>); y++; counter++; goto LABEL_1<0,3> } } + +// Output: +// 0 +// 2 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_21.gno b/gnovm/tests/files/heap_alloc_gotoloop9_21.gno new file mode 100644 index 00000000000..fa27f3376e0 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_21.gno @@ -0,0 +1,31 @@ +package main + +func main() { + var counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + f1 := func() { + LABEL_1: + if counter == 2 { + return + } + x := counter + f = append(f, func() { println(x) }) + counter++ + goto LABEL_1 + } + + f1() +} + +// Preprocessed: +// file{ package main; func main() { var counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); f1 := func func(){ if counter == (const (2 int)) { return }; x := counter; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); counter++; goto LABEL_1<0,0> }; f1() } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_21a.gno b/gnovm/tests/files/heap_alloc_gotoloop9_21a.gno new file mode 100644 index 00000000000..48364ed4075 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_21a.gno @@ -0,0 +1,33 @@ +package main + +func main() { + var counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + f1 := func() { + LABEL_1: + if counter == 2 { + return + } + x := counter + func() { + f = append(f, func() { println(x) }) + }() + counter++ + goto LABEL_1 + } + + f1() +} + +// Preprocessed: +// file{ package main; func main() { var counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); f1 := func func(){ if counter == (const (2 int)) { return }; x := counter; func func(){ f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(2,0)>) }) }>(); counter++; goto LABEL_1<0,0> }; f1() } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_21b.gno b/gnovm/tests/files/heap_alloc_gotoloop9_21b.gno new file mode 100644 index 00000000000..8758608a5e6 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_21b.gno @@ -0,0 +1,34 @@ +package main + +func main() { + var counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + f1 := func() { + LABEL_1: + if counter == 2 { + return + } + + func() { + x := counter + f = append(f, func() { println(x) }) + }() + counter++ + goto LABEL_1 + } + + f1() +} + +// Preprocessed: +// file{ package main; func main() { var counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); f1 := func func(){ if counter == (const (2 int)) { return }; func func(){ x := counter; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x) }) }(); counter++; goto LABEL_1<0,0> }; f1() } } + +// Output: +// 0 +// 1 diff --git a/gnovm/tests/files/heap_alloc_gotoloop9_22.gno b/gnovm/tests/files/heap_alloc_gotoloop9_22.gno new file mode 100644 index 00000000000..cd283345869 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_gotoloop9_22.gno @@ -0,0 +1,33 @@ +package main + +func main() { + var y, counter int + var f []func() + defer func() { + for _, ff := range f { + ff() + } + }() + + for i := 0; i < 2; i++ { + counter = 0 + LABEL_1: + if counter == 2 { + continue + } + x := y + f = append(f, func() { println(x) }) + y++ + counter++ + goto LABEL_1 + } +} + +// Preprocessed: +// file{ package main; func main() { var y, counter (const-type int); var f []func(); defer func func(){ for _, ff := range f { ff() } }(); for i := (const (0 int)); i < (const (2 int)); i++ { counter = (const (0 int)); if counter == (const (2 int)) { continue }; x := y; f = (const (append func(x []func()(),args ...func()())(res []func()())))(f, func func(){ (const (println func(xs ...interface{})()))(x<~VPBlock(1,0)>) }>); y++; counter++; goto LABEL_1<0,1> } } } + +// Output: +// 0 +// 1 +// 2 +// 3 diff --git a/gnovm/tests/files/heap_alloc_range1.gno b/gnovm/tests/files/heap_alloc_range1.gno new file mode 100644 index 00000000000..34520729a06 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range1.gno @@ -0,0 +1,30 @@ +package main + +import "fmt" + +var s1 []*int + +func forLoopRef() { + defer func() { + for i, e := range s1 { + fmt.Printf("s1[%d] is: %d\n", i, *e) + } + }() + + s := []int{0, 1, 2} + for i, _ := range s { + s1 = append(s1, &i) + } +} + +func main() { + forLoopRef() +} + +// Preprocessed: +// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); s := [](const-type int){(const (0 int)), (const (1 int)), (const (2 int))}; for i, _ := range s { s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(i)) } }; func main() { forLoopRef() } } + +// Output: +// s1[0] is: 2 +// s1[1] is: 2 +// s1[2] is: 2 diff --git a/gnovm/tests/files/heap_alloc_range2.gno b/gnovm/tests/files/heap_alloc_range2.gno new file mode 100644 index 00000000000..34b7cc527af --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range2.gno @@ -0,0 +1,30 @@ +package main + +import "fmt" + +var s1 []*int + +func forLoopRef() { + defer func() { + for i, e := range s1 { + fmt.Printf("s1[%d] is: %d\n", i, *e) + } + }() + + s := []int{0, 1, 2} + for _, v := range s { + s1 = append(s1, &v) + } +} + +func main() { + forLoopRef() +} + +// Preprocessed: +// file{ package main; import fmt fmt; var s1 []*((const-type int)); func forLoopRef() { defer func func(){ for i, e := range s1 { fmt.Printf((const ("s1[%d] is: %d\n" string)), (const-type gonative{interface {}})(i), (const-type gonative{interface {}})(*(e))) } }(); s := [](const-type int){(const (0 int)), (const (1 int)), (const (2 int))}; for _, v := range s { s1 = (const (append func(x []*int,args ...*int)(res []*int)))(s1, &(v)) } }; func main() { forLoopRef() } } + +// Output: +// s1[0] is: 2 +// s1[1] is: 2 +// s1[2] is: 2 diff --git a/gnovm/tests/files/heap_alloc_range3.gno b/gnovm/tests/files/heap_alloc_range3.gno new file mode 100644 index 00000000000..032d0cf8b6d --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range3.gno @@ -0,0 +1,23 @@ +package main + +func main() { + s := []int{1, 2} + + f := func() { + for i, v := range s { + println(i) + println(v) + } + } + + f() +} + +// Preprocessed: +// file{ package main; func main() { s := [](const-type int){(const (1 int)), (const (2 int))}; f := func func(){ for i, v := range s { (const (println func(xs ...interface{})()))(i); (const (println func(xs ...interface{})()))(v) } }; f() } } + +// Output: +// 0 +// 1 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_range4.gno b/gnovm/tests/files/heap_alloc_range4.gno new file mode 100644 index 00000000000..2418184caca --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range4.gno @@ -0,0 +1,24 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); s := [](const-type int){(const (1 int)), (const (2 int)), (const (3 int))}; for i, _ := range s { x := i; f := func func() (const-type int){ return x<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_range4a.gno b/gnovm/tests/files/heap_alloc_range4a.gno new file mode 100644 index 00000000000..229d59d6011 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range4a.gno @@ -0,0 +1,26 @@ +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 { + if true { + return x + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); m := map[(const-type string)] (const-type int){(const ("a" string)): (const (1 int)), (const ("b" string)): (const (2 int))}; for _, v := range m { x := v; f := func func() (const-type int){ if (const (true bool)) { return x<~VPBlock(2,1)> }; return (const (0 int)) }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_range4a1.gno b/gnovm/tests/files/heap_alloc_range4a1.gno new file mode 100644 index 00000000000..7b126b0e530 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range4a1.gno @@ -0,0 +1,22 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); m := map[(const-type string)] (const-type int){(const ("a" string)): (const (1 int)), (const ("b" string)): (const (2 int))}; for _, v := range m { f := func func() (const-type int){ return v }; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 2 +// 2 diff --git a/gnovm/tests/files/heap_alloc_range4a2.gno b/gnovm/tests/files/heap_alloc_range4a2.gno new file mode 100644 index 00000000000..c54450f4619 --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range4a2.gno @@ -0,0 +1,32 @@ +package main + +func main() { + var fns []func() int + y := 0 + m := map[string]int{"a": 1, "b": 2} + for _, v := range m { + x := v + f := func() int { + switch y { + case 0: + if true { + return x + } + default: + return 0 + } + return 0 + } + fns = append(fns, f) + } + for _, fn := range fns { + println(fn()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); y := (const (0 int)); m := map[(const-type string)] (const-type int){(const ("a" string)): (const (1 int)), (const ("b" string)): (const (2 int))}; for _, v := range m { x := v; f := func func() (const-type int){ switch y { case (const (0 int)): if (const (true bool)) { return x<~VPBlock(3,1)> }; default: return (const (0 int)) }; return (const (0 int)) }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 1 +// 2 diff --git a/gnovm/tests/files/heap_alloc_range4b.gno b/gnovm/tests/files/heap_alloc_range4b.gno new file mode 100644 index 00000000000..b4a380b361b --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range4b.gno @@ -0,0 +1,26 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); s := (const ("hello" string)); for i, _ := range s { x := i; f := func func() (const-type int){ return x<~VPBlock(1,1)> }>; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 0 +// 1 +// 2 +// 3 +// 4 diff --git a/gnovm/tests/files/heap_alloc_range4b1.gno b/gnovm/tests/files/heap_alloc_range4b1.gno new file mode 100644 index 00000000000..24dd99ea98f --- /dev/null +++ b/gnovm/tests/files/heap_alloc_range4b1.gno @@ -0,0 +1,25 @@ +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()) + } +} + +// Preprocessed: +// file{ package main; func main() { var fns []func() (const-type int); s := (const ("hello" string)); for i, _ := range s { f := func func() (const-type int){ return i }; fns = (const (append func(x []func()( int),args ...func()( int))(res []func()( int))))(fns, f) }; for _, fn := range fns { (const (println func(xs ...interface{})()))(fn()) } } } + +// Output: +// 4 +// 4 +// 4 +// 4 +// 4 diff --git a/gnovm/tests/files/import6.gno b/gnovm/tests/files/import6.gno index 6909e1ad923..f3cd9930eb5 100644 --- a/gnovm/tests/files/import6.gno +++ b/gnovm/tests/files/import6.gno @@ -7,4 +7,4 @@ func main() { } // Error: -// github.com/gnolang/gno/_test/c2/c2.gno:3:1: import cycle detected: "github.com/gnolang/gno/_test/c1" (through [github.com/gnolang/gno/_test/c1 github.com/gnolang/gno/_test/c2]) +// github.com/gnolang/gno/_test/c2/c2.gno:3:8: import cycle detected: "github.com/gnolang/gno/_test/c1" (through [github.com/gnolang/gno/_test/c1 github.com/gnolang/gno/_test/c2]) diff --git a/gnovm/tests/files/recursive1.gno b/gnovm/tests/files/recursive1.gno index 8279e247d84..cd86d351dec 100644 --- a/gnovm/tests/files/recursive1.gno +++ b/gnovm/tests/files/recursive1.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/recursive1.gno:1:1: invalid recursive type: S -> S +// main/files/recursive1.gno:3:6: invalid recursive type: S -> S diff --git a/gnovm/tests/files/recursive1c.gno b/gnovm/tests/files/recursive1c.gno index 7797f375027..36237b039c0 100644 --- a/gnovm/tests/files/recursive1c.gno +++ b/gnovm/tests/files/recursive1c.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// main/files/recursive1c.gno:1:1: invalid recursive type: S -> S +// main/files/recursive1c.gno:5:6: invalid recursive type: S -> S diff --git a/gnovm/tests/files/recursive1d.gno b/gnovm/tests/files/recursive1d.gno index 22bf172b5ac..11c48bed3eb 100644 --- a/gnovm/tests/files/recursive1d.gno +++ b/gnovm/tests/files/recursive1d.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// main/files/recursive1d.gno:1:1: invalid recursive type: S -> S +// main/files/recursive1d.gno:5:6: invalid recursive type: S -> S diff --git a/gnovm/tests/files/recursive1f.gno b/gnovm/tests/files/recursive1f.gno index 81fe2a5699c..7c6bc8f52fe 100644 --- a/gnovm/tests/files/recursive1f.gno +++ b/gnovm/tests/files/recursive1f.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/recursive1f.gno:3:1: invalid recursive type: S -> S +// main/files/recursive1f.gno:4:7: invalid recursive type: S -> S diff --git a/gnovm/tests/files/recursive2.gno b/gnovm/tests/files/recursive2.gno index 4ed86f03d58..f2382d7ce1a 100644 --- a/gnovm/tests/files/recursive2.gno +++ b/gnovm/tests/files/recursive2.gno @@ -18,4 +18,4 @@ func main() { } // Error: -// main/files/recursive2.gno:1:1: invalid recursive type: A -> B -> C -> A +// main/files/recursive2.gno:3:6: invalid recursive type: A -> B -> C -> A diff --git a/gnovm/tests/files/recursive2c.gno b/gnovm/tests/files/recursive2c.gno index 3b5c27ed8ea..d6068fba1bc 100644 --- a/gnovm/tests/files/recursive2c.gno +++ b/gnovm/tests/files/recursive2c.gno @@ -18,4 +18,4 @@ func main() { } // Error: -// main/files/recursive2c.gno:3:1: name B not defined in fileset with files [files/recursive2c.gno] +// main/files/recursive2c.gno:4:7: name B not defined in fileset with files [files/recursive2c.gno] diff --git a/gnovm/tests/files/recursive4a.gno b/gnovm/tests/files/recursive4a.gno index 8b4d13b4785..3d2b4e33590 100644 --- a/gnovm/tests/files/recursive4a.gno +++ b/gnovm/tests/files/recursive4a.gno @@ -6,4 +6,4 @@ func main() { } // Error: -// main/files/recursive4a.gno:1:1: invalid recursive type: time -> time +// main/files/recursive4a.gno:3:6: invalid recursive type: time -> time diff --git a/gnovm/tests/files/recursive5.gno b/gnovm/tests/files/recursive5.gno index 1c2fbd89fb8..1498926f305 100644 --- a/gnovm/tests/files/recursive5.gno +++ b/gnovm/tests/files/recursive5.gno @@ -10,4 +10,4 @@ func main() { } // Error: -// main/files/recursive5.gno:1:1: invalid recursive type: S -> S +// main/files/recursive5.gno:3:6: invalid recursive type: S -> S diff --git a/gnovm/tests/files/recursive6a.gno b/gnovm/tests/files/recursive6a.gno index 8123fc626a5..a0791aca7f5 100644 --- a/gnovm/tests/files/recursive6a.gno +++ b/gnovm/tests/files/recursive6a.gno @@ -9,4 +9,4 @@ func main() { } // Error: -// main/files/recursive6a.gno:1:1: invalid recursive type: SelfReferencing -> SelfReferencing +// main/files/recursive6a.gno:3:6: invalid recursive type: SelfReferencing -> SelfReferencing diff --git a/gnovm/tests/files/recursive7a.gno b/gnovm/tests/files/recursive7a.gno index b3c57516f13..b27a3a01324 100644 --- a/gnovm/tests/files/recursive7a.gno +++ b/gnovm/tests/files/recursive7a.gno @@ -5,4 +5,4 @@ type S [2]S func main() {} // Error: -// main/files/recursive7a.gno:1:1: invalid recursive type: S -> S +// main/files/recursive7a.gno:3:6: invalid recursive type: S -> S diff --git a/gnovm/tests/files/recursive8.gno b/gnovm/tests/files/recursive8.gno index 1f9325ae35c..afd5b44fcc6 100644 --- a/gnovm/tests/files/recursive8.gno +++ b/gnovm/tests/files/recursive8.gno @@ -5,4 +5,4 @@ type Int Int func main() {} // Error: -// main/files/recursive8.gno:1:1: invalid recursive type: Int -> Int +// main/files/recursive8.gno:3:6: invalid recursive type: Int -> Int diff --git a/gnovm/tests/files/recursive9.gno b/gnovm/tests/files/recursive9.gno index 8181be55d33..3b930ee248d 100644 --- a/gnovm/tests/files/recursive9.gno +++ b/gnovm/tests/files/recursive9.gno @@ -5,4 +5,4 @@ type Int = Int func main() {} // Error: -// main/files/recursive9.gno:1:1: invalid recursive type: Int -> Int +// main/files/recursive9.gno:3:6: invalid recursive type: Int -> Int diff --git a/gnovm/tests/files/recursive9a.gno b/gnovm/tests/files/recursive9a.gno index b96efa090e4..acd11893bad 100644 --- a/gnovm/tests/files/recursive9a.gno +++ b/gnovm/tests/files/recursive9a.gno @@ -5,4 +5,4 @@ type Int = *Int func main() {} // Error: -// main/files/recursive9a.gno:1:1: invalid recursive type: Int -> Int \ No newline at end of file +// main/files/recursive9a.gno:3:6: invalid recursive type: Int -> Int diff --git a/gnovm/tests/files/recursive9b.gno b/gnovm/tests/files/recursive9b.gno index e033349d597..3901099ec60 100644 --- a/gnovm/tests/files/recursive9b.gno +++ b/gnovm/tests/files/recursive9b.gno @@ -5,4 +5,4 @@ type Int = func() Int func main() {} // Error: -// main/files/recursive9b.gno:1:1: invalid recursive type: Int -> Int \ No newline at end of file +// main/files/recursive9b.gno:3:6: invalid recursive type: Int -> Int diff --git a/gnovm/tests/files/recursive9c.gno b/gnovm/tests/files/recursive9c.gno index ad865978920..bf08d406dc2 100644 --- a/gnovm/tests/files/recursive9c.gno +++ b/gnovm/tests/files/recursive9c.gno @@ -5,4 +5,4 @@ type Int = []Int func main() {} // Error: -// main/files/recursive9c.gno:1:1: invalid recursive type: Int -> Int +// main/files/recursive9c.gno:3:6: invalid recursive type: Int -> Int diff --git a/gnovm/tests/files/recursive9d.gno b/gnovm/tests/files/recursive9d.gno index ae7310ede0f..7ea4c934c65 100644 --- a/gnovm/tests/files/recursive9d.gno +++ b/gnovm/tests/files/recursive9d.gno @@ -7,4 +7,4 @@ type S = struct { func main() {} // Error: -// main/files/recursive9d.gno:1:1: invalid recursive type: S -> S +// main/files/recursive9d.gno:3:6: invalid recursive type: S -> S diff --git a/gnovm/tests/files/switch13.gno b/gnovm/tests/files/switch13.gno index b2223c85256..f69f09d1037 100644 --- a/gnovm/tests/files/switch13.gno +++ b/gnovm/tests/files/switch13.gno @@ -14,4 +14,4 @@ func main() { } // Error: -// main/files/switch13.gno:0:0: i is not a type +// main/files/switch13.gno:8:0: i is not a type diff --git a/gnovm/tests/files/types/assign_literal11.gno b/gnovm/tests/files/types/assign_literal11.gno index 851fe74593d..ab916e4c752 100644 --- a/gnovm/tests/files/types/assign_literal11.gno +++ b/gnovm/tests/files/types/assign_literal11.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/types/assign_literal11.gno:6:2: cannot assign to (const (3.14 bigdec)) +// main/files/types/assign_literal11.gno:6:2: cannot assign to const Pi diff --git a/gnovm/tests/files/types/assign_literal3.gno b/gnovm/tests/files/types/assign_literal3.gno index e4a4441853d..ce051b0dcbb 100644 --- a/gnovm/tests/files/types/assign_literal3.gno +++ b/gnovm/tests/files/types/assign_literal3.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/types/assign_literal3.gno:4:2: cannot assign to (const (true bool)) +// main/files/types/assign_literal3.gno:4:2: cannot assign to uverse true diff --git a/gnovm/tests/files/types/assign_nil.gno b/gnovm/tests/files/types/assign_nil.gno index 8c756da3b60..ecbca26dad0 100644 --- a/gnovm/tests/files/types/assign_nil.gno +++ b/gnovm/tests/files/types/assign_nil.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/types/assign_nil.gno:7:2: cannot assign to (const (undefined)) +// main/files/types/assign_nil.gno:7:2: cannot assign to uverse nil diff --git a/gnovm/tests/files/types/assign_nil2.gno b/gnovm/tests/files/types/assign_nil2.gno index fd7d509fccc..a1559d9de1f 100644 --- a/gnovm/tests/files/types/assign_nil2.gno +++ b/gnovm/tests/files/types/assign_nil2.gno @@ -8,4 +8,4 @@ func main() { } // Error: -// main/files/types/assign_nil2.gno:7:2: cannot assign to (const (undefined)) +// main/files/types/assign_nil2.gno:7:2: cannot assign to uverse nil diff --git a/gnovm/tests/files/var18.gno b/gnovm/tests/files/var18.gno index f01d3871d39..475c45f6e38 100644 --- a/gnovm/tests/files/var18.gno +++ b/gnovm/tests/files/var18.gno @@ -5,4 +5,4 @@ func main() { } // Error: -// main/files/var18.gno:4:6: missing init expr for c +// main/files/var18.gno:4:6: missing init expr for c diff --git a/gnovm/tests/files/var19.gno b/gnovm/tests/files/var19.gno index 99d7452f603..2856d6cd05a 100644 --- a/gnovm/tests/files/var19.gno +++ b/gnovm/tests/files/var19.gno @@ -1,12 +1,11 @@ package main func main() { - var a, b, c = 1, a+1 - + var a, b, c = 1, a + 1 println(a) println(b) println(c) } // Error: -// main/files/var19.gno:4:6: missing init expr for c +// main/files/var19.gno:4:6: missing init expr for c diff --git a/gnovm/tests/files/var22.gno b/gnovm/tests/files/var22.gno index 3f85f0f156d..5b21f7aa5bc 100644 --- a/gnovm/tests/files/var22.gno +++ b/gnovm/tests/files/var22.gno @@ -11,4 +11,4 @@ func main() { } // Error: -// main/files/var22.gno:8:6: missing init expr for c +// main/files/var22.gno:8:6: missing init expr for c