diff --git a/_test/issue-993.go b/_test/issue-993.go new file mode 100644 index 00000000..d0cc62dc --- /dev/null +++ b/_test/issue-993.go @@ -0,0 +1,15 @@ +package main + +var m map[string]int64 + +func initVar() { + m = make(map[string]int64) +} + +func main() { + initVar() + println(len(m)) +} + +// Output: +// 0 diff --git a/_test/var15.go b/_test/var15.go new file mode 100644 index 00000000..b4d9e6fb --- /dev/null +++ b/_test/var15.go @@ -0,0 +1,15 @@ +package main + +var a int = 2 + +func inca() { + a = a + 1 +} + +func main() { + inca() + println(a) +} + +// Output: +// 3 diff --git a/interp/cfg.go b/interp/cfg.go index 1105c3d3..cb7da805 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -611,7 +611,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { } if n.anc.kind == constDecl { n.gen = nop - n.findex = -1 + n.findex = notInFrame if sym, _, ok := sc.lookup(dest.ident); ok { sym.kind = constSym } @@ -717,7 +717,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { // This operation involved constants, and the result is already computed // by constOp and available in n.rval. Nothing else to do at execution. n.gen = nop - n.findex = -1 + n.findex = notInFrame case n.anc.kind == assignStmt && n.anc.action == aAssign: // To avoid a copy in frame, if the result is to be assigned, store it directly // at the frame location of destination. @@ -858,7 +858,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { } switch { case n.typ.cat == builtinT: - n.findex = -1 + n.findex = notInFrame n.val = nil case n.anc.kind == returnStmt: // Store result directly to frame output location, to avoid a frame copy. @@ -900,7 +900,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { n.rval = c1.rval case c1.rval.IsValid() && isConstType(c0.typ): n.gen = nop - n.findex = -1 + n.findex = notInFrame n.typ = c0.typ if c, ok := c1.rval.Interface().(constant.Value); ok { i, _ := constant.Int64Val(constant.ToInt(c)) @@ -963,7 +963,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { } } } else { - n.findex = -1 + n.findex = notInFrame } } @@ -1044,7 +1044,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { case fileStmt: wireChild(n, varDecl) sc = sc.pop() - n.findex = -1 + n.findex = notInFrame case forStmt0: // for {} body := n.child[0] @@ -1502,6 +1502,9 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { // Resolve source package symbol if sym, ok := interp.srcPkg[pkg][name]; ok { n.findex = sym.index + if sym.global { + n.level = globalFrame + } n.val = sym.node n.gen = nop n.action = aGetSym @@ -1516,7 +1519,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { if n.child[0].isType(sc) { // Handle method as a function with receiver in 1st argument n.val = m - n.findex = -1 + n.findex = notInFrame n.gen = nop n.typ = &itype{} *n.typ = *m.typ @@ -1829,7 +1832,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { switch { case n.rval.IsValid(): n.gen = nop - n.findex = -1 + n.findex = notInFrame case n.anc.kind == assignStmt && n.anc.action == aAssign: dest := n.anc.child[childPos(n)-n.anc.nright] n.typ = dest.typ @@ -1856,6 +1859,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { if sc.global { // Global object allocation is already performed in GTA. index = sc.sym[c.ident].index + c.level = globalFrame } else { index = sc.add(n.typ) sc.sym[c.ident] = &symbol{index: index, kind: varSym, typ: n.typ} diff --git a/interp/gta.go b/interp/gta.go index e6d41cca..71c602b4 100644 --- a/interp/gta.go +++ b/interp/gta.go @@ -115,6 +115,7 @@ func (interp *Interpreter) gta(root *node, rpath, importPath string) ([]*node, e sc.sym[c.ident] = &symbol{index: sc.add(n.typ), kind: varSym, global: true, typ: n.typ, node: n} continue } + c.level = globalFrame // redeclaration error if sym.typ.node != nil && sym.typ.node.anc != nil { diff --git a/interp/interp.go b/interp/interp.go index 38f73191..c73b0604 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -65,7 +65,8 @@ type frame struct { // Located at start of struct to ensure proper aligment. id uint64 - anc *frame // ancestor frame (global space) + root *frame // global space + anc *frame // ancestor frame (caller space) data []reflect.Value // values mutex sync.RWMutex @@ -80,8 +81,11 @@ func newFrame(anc *frame, len int, id uint64) *frame { data: make([]reflect.Value, len), id: id, } - if anc != nil { + if anc == nil { + f.root = f + } else { f.done = anc.done + f.root = anc.root } return f } @@ -93,6 +97,7 @@ func (f *frame) clone() *frame { defer f.mutex.RUnlock() return &frame{ anc: f.anc, + root: f.root, data: f.data, deferred: f.deferred, recovered: f.recovered, @@ -239,7 +244,7 @@ type Options struct { func New(options Options) *Interpreter { i := Interpreter{ opt: opt{context: build.Default}, - frame: &frame{data: []reflect.Value{}}, + frame: newFrame(nil, 0, 0), fset: token.NewFileSet(), universe: initUniverse(), scopes: map[string]*scope{}, diff --git a/interp/run.go b/interp/run.go index 5561feab..3e4050b5 100644 --- a/interp/run.go +++ b/interp/run.go @@ -1221,6 +1221,8 @@ func call(n *node) { func getFrame(f *frame, l int) *frame { switch l { + case globalFrame: + return f.root case 0: return f case 1: diff --git a/interp/scope.go b/interp/scope.go index cae613f6..bfeeba56 100644 --- a/interp/scope.go +++ b/interp/scope.go @@ -130,6 +130,9 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) { level := s.level for { if sym, ok := s.sym[ident]; ok { + if sym.global { + return sym, globalFrame, true + } return sym, level - s.level, true } if s.anc == nil { diff --git a/interp/value.go b/interp/value.go index 660065bc..63830ec1 100644 --- a/interp/value.go +++ b/interp/value.go @@ -5,8 +5,15 @@ import ( "reflect" ) +const ( + notInFrame = -1 // value of node.findex for literal values (not in frame) + globalFrame = -1 // value of node.level for global symbols +) + func valueGenerator(n *node, i int) func(*frame) reflect.Value { switch n.level { + case globalFrame: + return func(f *frame) reflect.Value { return valueOf(f.root.data, i) } case 0: return func(f *frame) reflect.Value { return valueOf(f.data, i) } case 1: @@ -171,18 +178,16 @@ func genValue(n *node) func(*frame) reflect.Value { return func(f *frame) reflect.Value { return v } } if n.sym != nil { - if n.sym.index < 0 { + i := n.sym.index + if i < 0 { return genValue(n.sym.node) } - i := n.sym.index if n.sym.global { - return func(f *frame) reflect.Value { - return n.interp.frame.data[i] - } + return func(f *frame) reflect.Value { return f.root.data[i] } } return valueGenerator(n, i) } - if n.findex < 0 { + if n.findex == notInFrame { var v reflect.Value if w, ok := n.val.(reflect.Value); ok { v = w