diff --git a/go/types/check.go b/go/types/check.go index 7b659135..d413fe01 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -62,8 +62,7 @@ type checker struct { funcs []funcInfo // list of functions/methods with correct signatures and non-empty bodies 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) - initMap map[Object]*declInfo // map of variables/functions with init expressions/bodies + objMap map[Object]*declInfo // map of package-level objects to declaration info // context within which the current object is type-checked // (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) -// 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) { from := check.decl if from == nil { return // not in a package-level init expression } - init := check.initMap[to] - if init == nil { - return // to does not have a package-level init expression + decl := check.objMap[to] + if 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] = init + m[to] = decl } func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) { diff --git a/go/types/decl.go b/go/types/decl.go index 610ceee5..6cd4f235 100644 --- a/go/types/decl.go +++ b/go/types/decl.go @@ -43,12 +43,8 @@ func (check *checker) objDecl(obj Object, def *Named, path []*TypeName) { } d := check.objMap[obj] - if debug && d == nil { - if check.objMap == nil { - 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) + if d == nil { + check.dump("%s: %s should have been declared", obj.Pos(), obj) unreachable() } diff --git a/go/types/resolver.go b/go/types/resolver.go index 7d352350..07fbf774 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -29,6 +29,12 @@ type declInfo struct { 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 // have the appropriate number of names and init exprs. For const // 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 // base type names. - var ( - objMap = make(map[Object]*declInfo) // corresponding declaration info - initMap = make(map[Object]*declInfo) // declaration info for objects with initializers - ) + var objMap = make(map[Object]*declInfo) // corresponding declaration info // declare declares obj in the package scope, records its ident -> obj mapping, // 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} declare(name, obj, d) - - // all constants have an initializer - initMap[obj] = d } check.arityMatch(s, last) @@ -275,9 +275,9 @@ func (check *checker) resolveFiles(files []*ast.File) { lhs[i] = obj d := d1 - var init ast.Expr if d == nil { // individual assignments + var init ast.Expr if i < len(s.Values) { init = s.Values[i] } @@ -285,11 +285,6 @@ func (check *checker) resolveFiles(files []*ast.File) { } declare(name, obj, d) - - // remember variable if it has an initializer - if d1 != nil || init != nil { - initMap[obj] = d - } } check.arityMatch(s, nil) @@ -340,10 +335,6 @@ func (check *checker) resolveFiles(files []*ast.File) { } info := &declInfo{file: fileScope, fdecl: d} objMap[obj] = info - // remember function if it has a body (= initializer) - if d.Body != nil { - initMap[obj] = info - } default: 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. - check.initMap = initMap - check.objMap = objMap // indicate that we are checking package-level declarations (objects may not have a type yet) + check.objMap = objMap 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) { 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 // 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 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) { case *Const, *Var: - if init := initMap[obj]; init != nil { - check.dependencies(obj, init, initPath) + if d := objMap[obj]; d.hasInitializer() { + check.dependencies(obj, d, initPath) } } } - check.initMap = nil // not needed anymore // Phase 6: Check for declared but not used packages and function variables. // Note: must happen after checking all functions because function bodies