go/ssa: treat declared init functions less specially

Before this change, declared init functions were not package members;
this choice dates from when go/types did not create Func objects for them.

Now, they have an Object.  They appear in Members, keyed by "init#%d"
(sequence number) for uniqueness.  They can be enumerated.  They can
be looked up from a *types.Func via (*Program).FuncValue.

Caveat: fn.Object.Name() no longer equals fn.Name() in all cases.

NB: incompatible API change!  (Your build will not break though.)

Change-Id: I2de873079fd57329e6c2f55a282940f6699a77a1
Reviewed-on: https://go-review.googlesource.com/6950
Reviewed-by: Robert Griesemer <gri@golang.org>
Reviewed-by: Peter Collingbourne <pcc@google.com>
This commit is contained in:
Alan Donovan 2015-03-05 14:35:50 -05:00
parent 4744be3abc
commit 9957739054
5 changed files with 26 additions and 22 deletions

View File

@ -2125,24 +2125,12 @@ func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
if isBlankIdent(id) { if isBlankIdent(id) {
return // discard return // discard
} }
var fn *Function fn := pkg.values[pkg.info.Defs[id]].(*Function)
if decl.Recv == nil && id.Name == "init" { if decl.Recv == nil && id.Name == "init" {
pkg.ninit++
fn = &Function{
name: fmt.Sprintf("init#%d", pkg.ninit),
Signature: new(types.Signature),
pos: decl.Name.NamePos,
Pkg: pkg,
Prog: pkg.Prog,
syntax: decl,
}
var v Call var v Call
v.Call.Value = fn v.Call.Value = fn
v.setType(types.NewTuple()) v.setType(types.NewTuple())
pkg.init.emit(&v) pkg.init.emit(&v)
} else {
fn = pkg.values[pkg.info.Defs[id]].(*Function)
} }
b.buildFunction(fn) b.buildFunction(fn)
} }

View File

@ -8,6 +8,7 @@ package ssa
// See builder.go for explanation. // See builder.go for explanation.
import ( import (
"fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"os" "os"
@ -88,10 +89,15 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
pkg.Members[name] = g pkg.Members[name] = g
case *types.Func: case *types.Func:
sig := obj.Type().(*types.Signature)
if sig.Recv() == nil && name == "init" {
pkg.ninit++
name = fmt.Sprintf("init#%d", pkg.ninit)
}
fn := &Function{ fn := &Function{
name: name, name: name,
object: obj, object: obj,
Signature: obj.Type().(*types.Signature), Signature: sig,
syntax: syntax, syntax: syntax,
pos: obj.Pos(), pos: obj.Pos(),
Pkg: pkg, Pkg: pkg,
@ -102,7 +108,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
} }
pkg.values[obj] = fn pkg.values[obj] = fn
if fn.Signature.Recv() == nil { if sig.Recv() == nil {
pkg.Members[name] = fn // package-level function pkg.Members[name] = fn // package-level function
} }
@ -148,9 +154,6 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
case *ast.FuncDecl: case *ast.FuncDecl:
id := decl.Name id := decl.Name
if decl.Recv == nil && id.Name == "init" {
return // no object
}
if !isBlankIdent(id) { if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], decl) memberFromObject(pkg, pkg.info.Defs[id], decl)
} }

View File

@ -505,8 +505,13 @@ func sanityCheckPackage(pkg *Package) {
continue // not all members have typechecker objects continue // not all members have typechecker objects
} }
if obj.Name() != name { if obj.Name() != name {
panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s", if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
pkg.Object.Path(), mem, obj.Name(), name)) // Ok. The name of a declared init function varies between
// its types.Func ("init") and its ssa.Function ("init#%d").
} else {
panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
pkg.Object.Path(), mem, obj.Name(), name))
}
} }
if obj.Pos() != mem.Pos() { if obj.Pos() != mem.Pos() {
panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos())) panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos()))

View File

@ -144,7 +144,7 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
// - e is a reference to nil or a built-in function. // - e is a reference to nil or a built-in function.
// - the value was optimised away. // - the value was optimised away.
// //
// If e is an addressable expression used an an lvalue context, // If e is an addressable expression used in an lvalue context,
// value is the address denoted by e, and isAddr is true. // value is the address denoted by e, and isAddr is true.
// //
// The types of e (or &e, if isAddr) and the result are equal // The types of e (or &e, if isAddr) and the result are equal

View File

@ -40,10 +40,14 @@ type Program struct {
// declares. These may be accessed directly via Members, or via the // declares. These may be accessed directly via Members, or via the
// type-specific accessor methods Func, Type, Var and Const. // type-specific accessor methods Func, Type, Var and Const.
// //
// Members also contains entries for "init" (the synthetic package
// initializer) and "init#%d", the nth declared init function,
// and unspecified other things too.
//
type Package struct { type Package struct {
Prog *Program // the owning program Prog *Program // the owning program
Object *types.Package // the type checker's package object for this package Object *types.Package // the type checker's package object for this package
Members map[string]Member // all package members keyed by name Members map[string]Member // all package members keyed by name (incl. init and init#%d)
values map[types.Object]Value // package members (incl. types and methods), keyed by object values map[types.Object]Value // package members (incl. types and methods), keyed by object
init *Function // Func("init"); the package's init function init *Function // Func("init"); the package's init function
debug bool // include full debug info in this package debug bool // include full debug info in this package
@ -281,6 +285,10 @@ type Node interface {
// If the function is a method (Signature.Recv() != nil) then the first // If the function is a method (Signature.Recv() != nil) then the first
// element of Params is the receiver parameter. // element of Params is the receiver parameter.
// //
// A Go package may declare many functions called "init".
// For each one, Object().Name() returns "init" but Name() returns
// "init#1", etc, in declaration order.
//
// Pos() returns the declaring ast.FuncLit.Type.Func or the position // Pos() returns the declaring ast.FuncLit.Type.Func or the position
// of the ast.FuncDecl.Name, if the function was explicit in the // of the ast.FuncDecl.Name, if the function was explicit in the
// source. Synthetic wrappers, for which Synthetic != "", may share // source. Synthetic wrappers, for which Synthetic != "", may share