interp: support calling goto from sub-scope
As opposed to other symbols, goto labels must be searched in included scopes, not upper ones. Implement scope.lookdown to perform this, to allow calls to goto to be embedded in included scopes where label is defined. Fixes #953.
This commit is contained in:
12
_test/goto1.go
Normal file
12
_test/goto1.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
if true {
|
||||
goto here
|
||||
}
|
||||
here:
|
||||
println("ok")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ok
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user