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

View File

@ -25,7 +25,7 @@ type declInfo struct {
init ast.Expr // init expression, or nil init ast.Expr // init expression, or nil
fdecl *ast.FuncDecl // func declaration, or nil fdecl *ast.FuncDecl // func declaration, or nil
deps map[Object]*declInfo // init dependencies; lazily allocated deps map[Object]bool // init dependencies; lazily allocated
mark int // see check.dependencies mark int // see check.dependencies
} }
@ -35,6 +35,16 @@ func (d *declInfo) hasInitializer() bool {
return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil 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 // arityMatch checks that the lhs and rhs of a const or var decl
// have the appropriate number of names and init exprs. For const // have the appropriate number of names and init exprs. For const
// decls, init is the value spec providing the init exprs; for // 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 { for _, obj := range objList {
switch obj.(type) { switch obj.(type) {
case *Const, *Var: case *Const, *Var:
if d := check.objMap[obj]; d.hasInitializer() { if check.objMap[obj].hasInitializer() {
check.dependencies(obj, d, initPath) check.dependencies(obj, initPath)
} }
} }
} }
@ -427,16 +437,11 @@ func (check *checker) unusedImports() {
} }
} }
// TODO(gri) Instead of this support function, introduce type func setObjects(set map[Object]bool) []Object {
// that combines an map[Object]*declInfo and []Object so that list := make([]Object, len(set))
// 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))
i := 0 i := 0
for obj := range m { for obj := range set {
// we don't care about the map element value
list[i] = obj list[i] = obj
i++ i++
} }
@ -444,6 +449,11 @@ func objectsOf(m map[Object]*declInfo) []Object {
return list 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. // inSourceOrder implements the sort.Sort interface.
type inSourceOrder []Object 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. // 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. // 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 // Path contains all nodes on the path to the current node obj (excluding obj).
// on the path to the current node (excluding the current node).
// //
// To detect cyles, the nodes are marked as follows: Initially, all nodes are unmarked // 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 // (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 // start of the cycle in the path. The tail of the path (path[mark-1:]) contains all
// nodes of the cycle. // 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 { if init.mark < 0 {
return // finished return // finished
} }
@ -516,9 +526,8 @@ func (check *checker) dependencies(obj Object, init *declInfo, path []Object) {
path = append(path, obj) // len(path) > 0 path = append(path, obj) // len(path) > 0
init.mark = len(path) // init.mark > 0 init.mark = len(path) // init.mark > 0
for _, obj := range objectsOf(init.deps) { for _, obj := range setObjects(init.deps) {
dep := init.deps[obj] check.dependencies(obj, path)
check.dependencies(obj, dep, path)
} }
init.mark = -1 // init.mark < 0 init.mark = -1 // init.mark < 0