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:
parent
6bd3206b1f
commit
f06b7e1853
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue