diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index 8ee158c5f7..fa09cb5b96 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -191,7 +191,7 @@ func genhash(sym *types.Sym, t *types.Type) { lineno = autogeneratedPos // less confusing than end of input dclcontext = PEXTERN - markdcl() + types.Markdcl(lineno) // func sym(p *T, h uintptr) uintptr fn := nod(ODCLFUNC, nil, nil) @@ -305,7 +305,7 @@ func genhash(sym *types.Sym, t *types.Type) { fn = typecheck(fn, Etop) typecheckslice(fn.Nbody.Slice(), Etop) Curfn = nil - popdcl() + types.Popdcl() if debug_dclstack != 0 { testdclstack() } @@ -369,7 +369,7 @@ func geneq(sym *types.Sym, t *types.Type) { lineno = autogeneratedPos // less confusing than end of input dclcontext = PEXTERN - markdcl() + types.Markdcl(lineno) // func sym(p, q *T) bool fn := nod(ODCLFUNC, nil, nil) @@ -497,7 +497,7 @@ func geneq(sym *types.Sym, t *types.Type) { fn = typecheck(fn, Etop) typecheckslice(fn.Nbody.Slice(), Etop) Curfn = nil - popdcl() + types.Popdcl() if debug_dclstack != 0 { testdclstack() } diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 64f410fa80..ea9c02dea8 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -1091,54 +1091,54 @@ func (p *importer) node() *Node { return nodl(p.pos(), op, p.expr(), nil) case OIF: - markdcl() + types.Markdcl(lineno) n := nodl(p.pos(), OIF, nil, nil) n.Ninit.Set(p.stmtList()) n.Left = p.expr() n.Nbody.Set(p.stmtList()) n.Rlist.Set(p.stmtList()) - popdcl() + types.Popdcl() return n case OFOR: - markdcl() + types.Markdcl(lineno) n := nodl(p.pos(), OFOR, nil, nil) n.Ninit.Set(p.stmtList()) n.Left, n.Right = p.exprsOrNil() n.Nbody.Set(p.stmtList()) - popdcl() + types.Popdcl() return n case ORANGE: - markdcl() + types.Markdcl(lineno) n := nodl(p.pos(), ORANGE, nil, nil) n.List.Set(p.stmtList()) n.Right = p.expr() n.Nbody.Set(p.stmtList()) - popdcl() + types.Popdcl() return n case OSELECT, OSWITCH: - markdcl() + types.Markdcl(lineno) n := nodl(p.pos(), op, nil, nil) n.Ninit.Set(p.stmtList()) n.Left, _ = p.exprsOrNil() n.List.Set(p.stmtList()) - popdcl() + types.Popdcl() return n // case OCASE, OXCASE: // unreachable - mapped to OXCASE case below by exporter case OXCASE: - markdcl() + types.Markdcl(lineno) n := nodl(p.pos(), OXCASE, nil, nil) - n.Xoffset = int64(block) + n.Xoffset = int64(types.Block) n.List.Set(p.exprList()) // TODO(gri) eventually we must declare variables for type switch // statements (type switch statements are not yet exported) n.Nbody.Set(p.stmtList()) - popdcl() + types.Popdcl() return n // case OFALL: @@ -1146,7 +1146,7 @@ func (p *importer) node() *Node { case OXFALL: n := nodl(p.pos(), OXFALL, nil, nil) - n.Xoffset = int64(block) + n.Xoffset = int64(types.Block) return n case OBREAK, OCONTINUE: @@ -1162,7 +1162,7 @@ func (p *importer) node() *Node { case OGOTO, OLABEL: n := nodl(p.pos(), op, newname(p.expr().Sym), nil) - n.Sym = dclstack // context, for goto restrictions + n.Sym = types.Dclstack // context, for goto restrictions return n case OEND: diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 0704d6ef5d..222881771d 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -15,99 +15,12 @@ import ( var externdcl []*Node -var blockgen int32 // max block number - -var block int32 // current block number - -// dclstack maintains a stack of shadowed symbol declarations so that -// popdcl can restore their declarations when a block scope ends. -// The stack is maintained as a linked list, using Sym's Link field. -// -// In practice, the "stack" actually ends up forming a tree: goto and label -// statements record the current state of dclstack so that checkgoto can -// validate that a goto statement does not jump over any declarations or -// into a new block scope. -// -// Finally, the Syms in this list are not "real" Syms as they don't actually -// represent object names. Sym is just a convenient type for saving shadowed -// Sym definitions, and only a subset of its fields are actually used. -var dclstack *types.Sym - -func dcopy(a, b *types.Sym) { - a.Pkg = b.Pkg - a.Name = b.Name - a.Def = b.Def - a.Block = b.Block - a.Lastlineno = b.Lastlineno -} - -func push() *types.Sym { - d := new(types.Sym) - d.Lastlineno = lineno - d.Link = dclstack - dclstack = d - return d -} - -// pushdcl pushes the current declaration for symbol s (if any) so that -// it can be shadowed by a new declaration within a nested block scope. -func pushdcl(s *types.Sym) *types.Sym { - d := push() - dcopy(d, s) - return d -} - -// popdcl pops the innermost block scope and restores all symbol declarations -// to their previous state. -func popdcl() { - d := dclstack - for ; d != nil && d.Name != ""; d = d.Link { - s := d.Pkg.Lookup(d.Name) - lno := s.Lastlineno - dcopy(s, d) - d.Lastlineno = lno - } - - if d == nil { - Fatalf("popdcl: no mark") - } - - dclstack = d.Link // pop mark - block = d.Block -} - -// markdcl records the start of a new block scope for declarations. -func markdcl() { - d := push() - d.Name = "" // used as a mark in fifo - d.Block = block - - blockgen++ - block = blockgen -} - -// keep around for debugging -func dumpdclstack() { - i := 0 - for d := dclstack; d != nil; d = d.Link { - fmt.Printf("%6d %p", i, d) - if d.Name != "" { - fmt.Printf(" '%s' %v\n", d.Name, d.Pkg.Lookup(d.Name)) - } else { - fmt.Printf(" ---\n") - } - i++ - } -} - func testdclstack() { - for d := dclstack; d != nil; d = d.Link { - if d.Name == "" { - if nerrors != 0 { - errorexit() - } - Fatalf("mark left on the stack") + if !types.IsDclstackValid() { + if nerrors != 0 { + errorexit() } + Fatalf("mark left on the dclstack") } } @@ -191,7 +104,7 @@ func declare(n *Node, ctxt Class) { vargen++ gen = vargen } - pushdcl(s) + types.Pushdcl(s, lineno) n.Name.Curfn = Curfn } @@ -199,7 +112,7 @@ func declare(n *Node, ctxt Class) { n.Xoffset = 0 } - if s.Block == block { + if s.Block == types.Block { // functype will print errors about duplicate function arguments. // Don't repeat the error here. if ctxt != PPARAM && ctxt != PPARAMOUT { @@ -207,7 +120,7 @@ func declare(n *Node, ctxt Class) { } } - s.Block = block + s.Block = types.Block s.Lastlineno = lineno s.Def = asTypesNode(n) n.Name.Vargen = int32(gen) @@ -418,7 +331,7 @@ func colasdefn(left []*Node, defn *Node) { } n.Sym.SetUniq(false) - if n.Sym.Block == block { + if n.Sym.Block == types.Block { continue } @@ -597,7 +510,7 @@ var funcdepth int32 // len(funcstack) during parsing, but then forced to be th // start the function. // called before funcargs; undone at end of funcbody. func funcstart(n *Node) { - markdcl() + types.Markdcl(lineno) funcstack = append(funcstack, Curfn) funcdepth++ Curfn = n @@ -611,7 +524,7 @@ func funcbody(n *Node) { if dclcontext != PAUTO { Fatalf("funcbody: unexpected dclcontext %d", dclcontext) } - popdcl() + types.Popdcl() funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1] funcdepth-- if funcdepth == 0 { diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 00d44d885b..22314b7b2c 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -381,7 +381,6 @@ func Main(archInit func(*Arch)) { initUniverse() - blockgen = 1 dclcontext = PEXTERN nerrors = 0 @@ -756,7 +755,7 @@ func findpkg(name string) (file string, ok bool) { // so that the compiler can generate calls to them, // but does not make them visible to user code. func loadsys() { - block = 1 + types.Block = 1 inimport = true typecheckok = true diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index bd769b6126..1ad19df7df 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -85,7 +85,7 @@ type linkname struct { } func (p *noder) node() { - block = 1 + types.Block = 1 imported_unsafe = false p.lineno(p.file.PkgName) @@ -726,10 +726,10 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node { n.Left = p.newname(stmt.Label) } if op == OGOTO { - n.Sym = dclstack // context, for goto restriction + n.Sym = types.Dclstack // context, for goto restriction } if op == OXFALL { - n.Xoffset = int64(block) + n.Xoffset = int64(types.Block) } return n case *syntax.CallStmt: @@ -777,14 +777,14 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node { } func (p *noder) blockStmt(stmt *syntax.BlockStmt) []*Node { - markdcl() + types.Markdcl(lineno) nodes := p.stmts(stmt.List) - popdcl() + types.Popdcl() return nodes } func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node { - markdcl() + types.Markdcl(lineno) n := p.nod(stmt, OIF, nil, nil) if stmt.Init != nil { n.Ninit.Set1(p.stmt(stmt.Init)) @@ -801,12 +801,12 @@ func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node { n.Rlist.Set1(e) } } - popdcl() + types.Popdcl() return n } func (p *noder) forStmt(stmt *syntax.ForStmt) *Node { - markdcl() + types.Markdcl(lineno) var n *Node if r, ok := stmt.Init.(*syntax.RangeClause); ok { if stmt.Cond != nil || stmt.Post != nil { @@ -835,12 +835,12 @@ func (p *noder) forStmt(stmt *syntax.ForStmt) *Node { } } n.Nbody.Set(p.blockStmt(stmt.Body)) - popdcl() + types.Popdcl() return n } func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node { - markdcl() + types.Markdcl(lineno) n := p.nod(stmt, OSWITCH, nil, nil) if stmt.Init != nil { n.Ninit.Set1(p.stmt(stmt.Init)) @@ -856,7 +856,7 @@ func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node { n.List.Set(p.caseClauses(stmt.Body, tswitch)) - popdcl() + types.Popdcl() return n } @@ -864,7 +864,7 @@ func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node var nodes []*Node for _, clause := range clauses { p.lineno(clause) - markdcl() + types.Markdcl(lineno) n := p.nod(clause, OXCASE, nil, nil) if clause.Cases != nil { n.List.Set(p.exprList(clause.Cases)) @@ -876,9 +876,9 @@ func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node // keep track of the instances for reporting unused nn.Name.Defn = tswitch } - n.Xoffset = int64(block) + n.Xoffset = int64(types.Block) n.Nbody.Set(p.stmts(clause.Body)) - popdcl() + types.Popdcl() nodes = append(nodes, n) } return nodes @@ -894,14 +894,14 @@ func (p *noder) commClauses(clauses []*syntax.CommClause) []*Node { var nodes []*Node for _, clause := range clauses { p.lineno(clause) - markdcl() + types.Markdcl(lineno) n := p.nod(clause, OXCASE, nil, nil) if clause.Comm != nil { n.List.Set1(p.stmt(clause.Comm)) } - n.Xoffset = int64(block) + n.Xoffset = int64(types.Block) n.Nbody.Set(p.stmts(clause.Body)) - popdcl() + types.Popdcl() nodes = append(nodes, n) } return nodes @@ -909,7 +909,7 @@ func (p *noder) commClauses(clauses []*syntax.CommClause) []*Node { func (p *noder) labeledStmt(label *syntax.LabeledStmt) *Node { lhs := p.nod(label, OLABEL, p.newname(label.Label), nil) - lhs.Sym = dclstack + lhs.Sym = types.Dclstack var ls *Node if label.Stmt != nil { // TODO(mdempsky): Should always be present. diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index ddf6f77c07..d9fdd76f23 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -1687,7 +1687,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym, iface lineno = autogeneratedPos dclcontext = PEXTERN - markdcl() + types.Markdcl(lineno) this := namedfield(".this", rcvr) this.Left.Name.Param.Ntype = this.Right @@ -1784,7 +1784,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym, iface funcbody(fn) Curfn = fn - popdcl() + types.Popdcl() if debug_dclstack != 0 { testdclstack() } diff --git a/src/cmd/compile/internal/types/scope.go b/src/cmd/compile/internal/types/scope.go new file mode 100644 index 0000000000..dfb63e9352 --- /dev/null +++ b/src/cmd/compile/internal/types/scope.go @@ -0,0 +1,105 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "cmd/internal/src" + "fmt" +) + +// Declaration stack & operations + +var blockgen int32 = 1 // max block number +var Block int32 // current block number + +// Dclstack maintains a stack of shadowed symbol declarations so that +// popdcl can restore their declarations when a block scope ends. +// The stack is maintained as a linked list, using Sym's Link field. +// +// In practice, the "stack" actually ends up forming a tree: goto and label +// statements record the current state of Dclstack so that checkgoto can +// validate that a goto statement does not jump over any declarations or +// into a new block scope. +// +// Finally, the Syms in this list are not "real" Syms as they don't actually +// represent object names. Sym is just a convenient type for saving shadowed +// Sym definitions, and only a subset of its fields are actually used. +var Dclstack *Sym + +func dcopy(a, b *Sym) { + a.Pkg = b.Pkg + a.Name = b.Name + a.Def = b.Def + a.Block = b.Block + a.Lastlineno = b.Lastlineno +} + +func push(pos src.XPos) *Sym { + d := new(Sym) + d.Lastlineno = pos + d.Link = Dclstack + Dclstack = d + return d +} + +// Pushdcl pushes the current declaration for symbol s (if any) so that +// it can be shadowed by a new declaration within a nested block scope. +func Pushdcl(s *Sym, pos src.XPos) *Sym { + d := push(pos) + dcopy(d, s) + return d +} + +// Popdcl pops the innermost block scope and restores all symbol declarations +// to their previous state. +func Popdcl() { + d := Dclstack + for ; d != nil && d.Name != ""; d = d.Link { + s := d.Pkg.Lookup(d.Name) + lno := s.Lastlineno + dcopy(s, d) + d.Lastlineno = lno + } + + if d == nil { + Fatalf("popdcl: no mark") + } + + Dclstack = d.Link // pop mark + Block = d.Block +} + +// Markdcl records the start of a new block scope for declarations. +func Markdcl(lineno src.XPos) { + d := push(lineno) + d.Name = "" // used as a mark in fifo + d.Block = Block + + blockgen++ + Block = blockgen +} + +// keep around for debugging +func DumpDclstack() { + i := 0 + for d := Dclstack; d != nil; d = d.Link { + fmt.Printf("%6d %p", i, d) + if d.Name != "" { + fmt.Printf(" '%s' %v\n", d.Name, d.Pkg.Lookup(d.Name)) + } else { + fmt.Printf(" ---\n") + } + i++ + } +} + +func IsDclstackValid() bool { + for d := Dclstack; d != nil; d = d.Link { + if d.Name == "" { + return false + } + } + return true +}