diff --git a/_test/goto1.go b/_test/goto1.go new file mode 100644 index 000000000..517cb5d6a --- /dev/null +++ b/_test/goto1.go @@ -0,0 +1,12 @@ +package main + +func main() { + if true { + goto here + } +here: + println("ok") +} + +// Output: +// ok diff --git a/interp/cfg.go b/interp/cfg.go index a4f9e1a6e..de1c4849f 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -194,7 +194,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { case breakStmt, continueStmt, gotoStmt: if len(n.child) > 0 { - // Handle labeled statements + // Handle labeled statements. label := n.child[0].ident if sym, _, ok := sc.lookup(label); ok { if sym.kind != labelSym { @@ -211,25 +211,23 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { case labeledStmt: label := n.child[0].ident - if sym, _, ok := sc.lookup(label); ok { - if sym.kind != labelSym { - err = n.child[0].cfgErrorf("label %s not defined", label) - break - } + // TODO(marc): labels must be stored outside of symbols to avoid collisions + // Used labels are searched in current and sub scopes, not upper ones. + if sym, ok := sc.lookdown(label); ok { sym.node = n n.sym = sym } else { n.sym = &symbol{kind: labelSym, node: n, index: -1} - sc.sym[label] = n.sym } + sc.sym[label] = n.sym case caseClause: sc = sc.pushBloc() if sn := n.anc.anc; sn.kind == typeSwitch && sn.child[1].action == aAssign { - // Type switch clause with a var defined in switch guard + // Type switch clause with a var defined in switch guard. var typ *itype if len(n.child) == 2 { - // 1 type in clause: define the var with this type in the case clause scope + // 1 type in clause: define the var with this type in the case clause scope. switch { case n.child[0].ident == nilIdent: typ = sc.getType("interface{}") @@ -239,7 +237,7 @@ func (interp *Interpreter) cfg(root *node, importPath string) ([]*node, error) { typ, err = nodeType(interp, sc, n.child[0]) } } else { - // define the var with the type in the switch guard expression + // Define the var with the type in the switch guard expression. typ = sn.child[1].child[1].child[0].typ } if err != nil { diff --git a/interp/scope.go b/interp/scope.go index 7dcbe60ad..cae613f65 100644 --- a/interp/scope.go +++ b/interp/scope.go @@ -72,26 +72,28 @@ type symbol struct { // execution to the index in frame, created exactly from the types layout. // type scope struct { - anc *scope // Ancestor upper scope + anc *scope // ancestor upper scope + child []*scope // included scopes def *node // function definition node this scope belongs to, or nil loop *node // loop exit node for break statement loopRestart *node // loop restart node for continue statement pkgID string // unique id of package in which scope is defined - types []reflect.Type // Frame layout, may be shared by same level scopes - level int // Frame level: number of frame indirections to access var during execution - sym map[string]*symbol // Map of symbols defined in this current scope + types []reflect.Type // frame layout, may be shared by same level scopes + level int // frame level: number of frame indirections to access var during execution + sym map[string]*symbol // map of symbols defined in this current scope global bool // true if scope refers to global space (single frame for universe and package level scopes) iota int // iota value in this scope } -// push creates a new scope and chain it to the current one. +// push creates a new child scope and chain it to the current one. func (s *scope) push(indirect bool) *scope { - sc := scope{anc: s, level: s.level, sym: map[string]*symbol{}} + sc := &scope{anc: s, level: s.level, sym: map[string]*symbol{}} + s.child = append(s.child, sc) if indirect { sc.types = []reflect.Type{} sc.level = s.level + 1 } else { - // propagate size, types, def and global as scopes at same level share the same frame + // Propagate size, types, def and global as scopes at same level share the same frame. sc.types = s.types sc.def = s.def sc.global = s.global @@ -99,7 +101,7 @@ func (s *scope) push(indirect bool) *scope { } // inherit loop state and pkgID from ancestor sc.loop, sc.loopRestart, sc.pkgID = s.loop, s.loopRestart, s.pkgID - return &sc + return sc } func (s *scope) pushBloc() *scope { return s.push(false) } @@ -107,7 +109,7 @@ func (s *scope) pushFunc() *scope { return s.push(true) } func (s *scope) pop() *scope { if s.level == s.anc.level { - // propagate size and types, as scopes at same level share the same frame + // Propagate size and types, as scopes at same level share the same frame. s.anc.types = s.types } return s.anc @@ -138,6 +140,20 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) { return nil, 0, false } +// lookdown searches for a symbol in the current scope and included ones, recursively. +// It returns the first found symbol and true, or nil and false. +func (s *scope) lookdown(ident string) (*symbol, bool) { + if sym, ok := s.sym[ident]; ok { + return sym, true + } + for _, c := range s.child { + if sym, ok := c.lookdown(ident); ok { + return sym, true + } + } + return nil, false +} + func (s *scope) rangeChanType(n *node) *itype { if sym, _, found := s.lookup(n.child[1].ident); found { if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {