go.tools/go/types: remove need for global initializer map

Another step towards resolving issue 7114.

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/72260043
This commit is contained in:
Robert Griesemer 2014-03-07 11:03:08 -08:00
parent 6bd3206b1f
commit f06b7e1853
3 changed files with 20 additions and 37 deletions

View File

@ -62,8 +62,7 @@ type checker struct {
funcs []funcInfo // list of functions/methods with correct signatures and non-empty bodies funcs []funcInfo // list of functions/methods with correct signatures and non-empty bodies
delayed []func() // delayed checks that require fully setup types delayed []func() // delayed checks that require fully setup types
objMap map[Object]*declInfo // if set we are in the package-level declaration phase (otherwise all objects seen must be declared) objMap map[Object]*declInfo // map of package-level objects to declaration info
initMap map[Object]*declInfo // map of variables/functions with init expressions/bodies
// context within which the current object is type-checked // context within which the current object is type-checked
// (valid only for the duration of type-checking a specific object) // (valid only for the duration of type-checking a specific object)
@ -192,22 +191,22 @@ func (check *checker) files(files []*ast.File) (err error) {
} }
// addDeclDep adds the dependency edge (check.decl -> to) // addDeclDep adds the dependency edge (check.decl -> to)
// if check.decl exists and to has an init expression. // if check.decl exists and to has an initializer.
func (check *checker) addDeclDep(to Object) { func (check *checker) addDeclDep(to Object) {
from := check.decl from := check.decl
if from == nil { if from == nil {
return // not in a package-level init expression return // not in a package-level init expression
} }
init := check.initMap[to] decl := check.objMap[to]
if init == nil { if decl == nil || !decl.hasInitializer() {
return // to does not have a package-level init expression return // to is not a package-level object or has no initializer
} }
m := from.deps m := from.deps
if m == nil { if m == nil {
m = make(map[Object]*declInfo) m = make(map[Object]*declInfo)
from.deps = m from.deps = m
} }
m[to] = init m[to] = decl
} }
func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) { func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) {

View File

@ -43,12 +43,8 @@ func (check *checker) objDecl(obj Object, def *Named, path []*TypeName) {
} }
d := check.objMap[obj] d := check.objMap[obj]
if debug && d == nil { if d == nil {
if check.objMap == nil { check.dump("%s: %s should have been declared", obj.Pos(), obj)
check.dump("%s: %s should have been declared (we are inside a function)", obj.Pos(), obj)
unreachable()
}
check.dump("%s: %s should have been forward-declared", obj.Pos(), obj)
unreachable() unreachable()
} }

View File

@ -29,6 +29,12 @@ type declInfo struct {
mark int // see check.dependencies mark int // see check.dependencies
} }
// hasInitializer reports whether the declared object has an initialization
// expression or function body.
func (d *declInfo) hasInitializer() bool {
return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
}
// 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
@ -87,10 +93,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
// independent of source order. Associate methods with receiver // independent of source order. Associate methods with receiver
// base type names. // base type names.
var ( var objMap = make(map[Object]*declInfo) // corresponding declaration info
objMap = make(map[Object]*declInfo) // corresponding declaration info
initMap = make(map[Object]*declInfo) // declaration info for objects with initializers
)
// declare declares obj in the package scope, records its ident -> obj mapping, // declare declares obj in the package scope, records its ident -> obj mapping,
// and updates objMap. The object must not be a function or method. // and updates objMap. The object must not be a function or method.
@ -248,9 +251,6 @@ func (check *checker) resolveFiles(files []*ast.File) {
d := &declInfo{file: fileScope, typ: last.Type, init: init} d := &declInfo{file: fileScope, typ: last.Type, init: init}
declare(name, obj, d) declare(name, obj, d)
// all constants have an initializer
initMap[obj] = d
} }
check.arityMatch(s, last) check.arityMatch(s, last)
@ -275,9 +275,9 @@ func (check *checker) resolveFiles(files []*ast.File) {
lhs[i] = obj lhs[i] = obj
d := d1 d := d1
var init ast.Expr
if d == nil { if d == nil {
// individual assignments // individual assignments
var init ast.Expr
if i < len(s.Values) { if i < len(s.Values) {
init = s.Values[i] init = s.Values[i]
} }
@ -285,11 +285,6 @@ func (check *checker) resolveFiles(files []*ast.File) {
} }
declare(name, obj, d) declare(name, obj, d)
// remember variable if it has an initializer
if d1 != nil || init != nil {
initMap[obj] = d
}
} }
check.arityMatch(s, nil) check.arityMatch(s, nil)
@ -340,10 +335,6 @@ func (check *checker) resolveFiles(files []*ast.File) {
} }
info := &declInfo{file: fileScope, fdecl: d} info := &declInfo{file: fileScope, fdecl: d}
objMap[obj] = info objMap[obj] = info
// remember function if it has a body (= initializer)
if d.Body != nil {
initMap[obj] = info
}
default: default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
@ -364,13 +355,11 @@ func (check *checker) resolveFiles(files []*ast.File) {
// Phase 3: Typecheck all objects in objMap, but not function bodies. // Phase 3: Typecheck all objects in objMap, but not function bodies.
check.initMap = initMap check.objMap = objMap
check.objMap = objMap // indicate that we are checking package-level declarations (objects may not have a type yet)
typePath := make([]*TypeName, 0, 8) // pre-allocate space for type declaration paths so that the underlying array is reused typePath := make([]*TypeName, 0, 8) // pre-allocate space for type declaration paths so that the underlying array is reused
for _, obj := range objectsOf(check.objMap) { for _, obj := range objectsOf(check.objMap) {
check.objDecl(obj, nil, typePath) check.objDecl(obj, nil, typePath)
} }
check.objMap = nil // not needed anymore
// At this point we may have a non-empty check.methods map; this means that not all // At this point we may have a non-empty check.methods map; this means that not all
// entries were deleted at the end of typeDecl because the respective receiver base // entries were deleted at the end of typeDecl because the respective receiver base
@ -389,15 +378,14 @@ func (check *checker) resolveFiles(files []*ast.File) {
// may introduce dependencies // may introduce dependencies
initPath := make([]Object, 0, 8) // pre-allocate space for initialization paths so that the underlying array is reused initPath := make([]Object, 0, 8) // pre-allocate space for initialization paths so that the underlying array is reused
for _, obj := range objectsOf(initMap) { for _, obj := range objectsOf(objMap) {
switch obj.(type) { switch obj.(type) {
case *Const, *Var: case *Const, *Var:
if init := initMap[obj]; init != nil { if d := objMap[obj]; d.hasInitializer() {
check.dependencies(obj, init, initPath) check.dependencies(obj, d, initPath)
} }
} }
} }
check.initMap = nil // not needed anymore
// Phase 6: Check for declared but not used packages and function variables. // Phase 6: Check for declared but not used packages and function variables.
// Note: must happen after checking all functions because function bodies // Note: must happen after checking all functions because function bodies