go.tools/go/types: simplified init dependency data structures

Preparation for fixing issue 7158.

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/83160045
This commit is contained in:
Robert Griesemer 2014-04-02 09:01:28 -07:00
parent fb8f3e8fbf
commit d7048bec64
2 changed files with 44 additions and 28 deletions

View File

@ -10,6 +10,7 @@ import (
"fmt"
"go/ast"
"go/token"
"sort"
"code.google.com/p/go.tools/go/exact"
)
@ -84,16 +85,10 @@ func (check *checker) addDeclDep(to Object) {
if from == nil {
return // not in a package-level init expression
}
decl := check.objMap[to]
if decl == nil || !decl.hasInitializer() {
if decl := check.objMap[to]; decl == nil || !decl.hasInitializer() {
return // to is not a package-level object or has no initializer
}
m := from.deps
if m == nil {
m = make(map[Object]*declInfo)
from.deps = m
}
m[to] = decl
from.addDep(to)
}
func (check *checker) assocMethod(tname string, meth *Func) {
@ -205,6 +200,17 @@ func (check *checker) handleBailout(err *error) {
}
}
func mapObjects(m map[Object]*declInfo) []Object {
list := make([]Object, len(m))
i := 0
for obj := range m {
list[i] = obj
i++
}
sort.Sort(inSourceOrder(list))
return list
}
// Files checks the provided files as part of the checker's package.
func (check *checker) Files(files []*ast.File) (err error) {
defer check.handleBailout(&err)
@ -213,7 +219,8 @@ func (check *checker) Files(files []*ast.File) (err error) {
check.collectObjects()
objList := objectsOf(check.objMap)
objList := mapObjects(check.objMap)
check.packageObjects(objList)
check.functionBodies()

View File

@ -25,8 +25,8 @@ type declInfo struct {
init ast.Expr // init expression, or nil
fdecl *ast.FuncDecl // func declaration, or nil
deps map[Object]*declInfo // init dependencies; lazily allocated
mark int // see check.dependencies
deps map[Object]bool // init dependencies; lazily allocated
mark int // see check.dependencies
}
// hasInitializer reports whether the declared object has an initialization
@ -35,6 +35,16 @@ func (d *declInfo) hasInitializer() bool {
return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
}
// addDep adds obj as a dependency to d.
func (d *declInfo) addDep(obj Object) {
m := d.deps
if m == nil {
m = make(map[Object]bool)
d.deps = m
}
m[obj] = true
}
// arityMatch checks that the lhs and rhs of a const or var decl
// have the appropriate number of names and init exprs. For const
// decls, init is the value spec providing the init exprs; for
@ -378,8 +388,8 @@ func (check *checker) initDependencies(objList []Object) {
for _, obj := range objList {
switch obj.(type) {
case *Const, *Var:
if d := check.objMap[obj]; d.hasInitializer() {
check.dependencies(obj, d, initPath)
if check.objMap[obj].hasInitializer() {
check.dependencies(obj, initPath)
}
}
}
@ -427,16 +437,11 @@ func (check *checker) unusedImports() {
}
}
// TODO(gri) Instead of this support function, introduce type
// that combines an map[Object]*declInfo and []Object so that
// we can encapsulate this and don't have to depend on correct
// Pos() information.
// objectsOf returns the keys of m in source order.
func objectsOf(m map[Object]*declInfo) []Object {
list := make([]Object, len(m))
func setObjects(set map[Object]bool) []Object {
list := make([]Object, len(set))
i := 0
for obj := range m {
for obj := range set {
// we don't care about the map element value
list[i] = obj
i++
}
@ -444,6 +449,11 @@ func objectsOf(m map[Object]*declInfo) []Object {
return list
}
// TODO(gri) Instead of this support function, introduce type
// that combines an map[Object]*declInfo and []Object so that
// we can encapsulate this and don't have to depend on correct
// Pos() information.
// inSourceOrder implements the sort.Sort interface.
type inSourceOrder []Object
@ -455,8 +465,7 @@ func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// manner and appends the encountered variables in postorder to the Info.InitOrder list.
// As a result, that list ends up being sorted topologically in the order of dependencies.
//
// The current node is represented by the pair (obj, init); and path contains all nodes
// on the path to the current node (excluding the current node).
// Path contains all nodes on the path to the current node obj (excluding obj).
//
// To detect cyles, the nodes are marked as follows: Initially, all nodes are unmarked
// (declInfo.mark == 0). On the way down, a node is appended to the path, and the node
@ -474,7 +483,8 @@ func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// start of the cycle in the path. The tail of the path (path[mark-1:]) contains all
// nodes of the cycle.
//
func (check *checker) dependencies(obj Object, init *declInfo, path []Object) {
func (check *checker) dependencies(obj Object, path []Object) {
init := check.objMap[obj]
if init.mark < 0 {
return // finished
}
@ -516,9 +526,8 @@ func (check *checker) dependencies(obj Object, init *declInfo, path []Object) {
path = append(path, obj) // len(path) > 0
init.mark = len(path) // init.mark > 0
for _, obj := range objectsOf(init.deps) {
dep := init.deps[obj]
check.dependencies(obj, dep, path)
for _, obj := range setObjects(init.deps) {
check.dependencies(obj, path)
}
init.mark = -1 // init.mark < 0