diff --git a/ssa/blockopt.go b/ssa/blockopt.go index f39635d0..71b5e564 100644 --- a/ssa/blockopt.go +++ b/ssa/blockopt.go @@ -147,7 +147,7 @@ func optimizeBlocks(f *Function) { if debugBlockOpt { f.DumpTo(os.Stderr) - MustSanityCheck(f, nil) + mustSanityCheck(f, nil) } for _, b := range f.Blocks { diff --git a/ssa/create.go b/ssa/create.go index f7ffa92e..d7d27480 100644 --- a/ssa/create.go +++ b/ssa/create.go @@ -85,23 +85,23 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.TypeName: - pkg.Members[name] = &Type{Object: obj} + pkg.Members[name] = &Type{object: obj} case *types.Const: pkg.Members[name] = &Constant{ - name: name, - Value: NewLiteral(obj.Val(), obj.Type(), obj.Pos()), - pos: obj.Pos(), + object: obj, + Value: NewLiteral(obj.Val(), obj.Type(), obj.Pos()), } case *types.Var: spec, _ := syntax.(*ast.ValueSpec) g := &Global{ - Pkg: pkg, - name: name, - typ: pointer(obj.Type()), // address - pos: obj.Pos(), - spec: spec, + Pkg: pkg, + name: name, + object: obj, + typ: pointer(obj.Type()), // address + pos: obj.Pos(), + spec: spec, } pkg.values[obj] = g pkg.Members[name] = g @@ -121,6 +121,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { sig := obj.Type().(*types.Signature) fn := &Function{ name: name, + object: obj, Signature: sig, Synthetic: synthetic, pos: obj.Pos(), // (iff syntax) @@ -264,4 +265,8 @@ func createPackage(prog *Program, importPath string, info *importer.PackageInfo) prog.PackagesByPath[importPath] = p prog.packages[p.Object] = p + + if prog.mode&SanityCheckFunctions != 0 { + sanityCheckPackage(p) + } } diff --git a/ssa/func.go b/ssa/func.go index c5017b3c..085dad6e 100644 --- a/ssa/func.go +++ b/ssa/func.go @@ -350,7 +350,7 @@ func (f *Function) finishBody() { } if f.Prog.mode&SanityCheckFunctions != 0 { - MustSanityCheck(f, nil) + mustSanityCheck(f, nil) } } diff --git a/ssa/promote.go b/ssa/promote.go index 8d5b58a1..a31d797f 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -285,6 +285,7 @@ func promotionWrapper(prog *Program, typ types.Type, cand *candidate) *Function if prog.mode&LogSource != 0 { defer logStack("promotionWrapper (%s)%s, type %s", typ, cand, sig)() } + // TODO(adonovan): is there a *types.Func for this function? fn := &Function{ name: cand.method.Name(), Signature: sig, @@ -395,6 +396,7 @@ func interfaceMethodWrapper(prog *Program, typ types.Type, id Id) *Function { } fn = &Function{ name: meth.Name(), + object: meth, Signature: meth.Type().(*types.Signature), Synthetic: fmt.Sprintf("interface method wrapper for %s.%s", typ, id), pos: meth.Pos(), @@ -495,6 +497,7 @@ func indirectionWrapper(meth *Function) *Function { s := meth.Signature recv := types.NewVar(token.NoPos, meth.Pkg.Object, "recv", types.NewPointer(s.Recv().Type())) + // TODO(adonovan): is there a *types.Func for this method? fn = &Function{ name: meth.Name(), Signature: types.NewSignature(recv, s.Params(), s.Results(), s.IsVariadic()), diff --git a/ssa/sanity.go b/ssa/sanity.go index 2feed7aa..3211d8cf 100644 --- a/ssa/sanity.go +++ b/ssa/sanity.go @@ -17,26 +17,26 @@ type sanity struct { insane bool } -// SanityCheck performs integrity checking of the SSA representation +// sanityCheck performs integrity checking of the SSA representation // of the function fn and returns true if it was valid. Diagnostics // are written to reporter if non-nil, os.Stderr otherwise. Some // diagnostics are only warnings and do not imply a negative result. // -// Sanity checking is intended to facilitate the debugging of code +// Sanity-checking is intended to facilitate the debugging of code // transformation passes. // -func SanityCheck(fn *Function, reporter io.Writer) bool { +func sanityCheck(fn *Function, reporter io.Writer) bool { if reporter == nil { reporter = os.Stderr } return (&sanity{reporter: reporter}).checkFunction(fn) } -// MustSanityCheck is like SanityCheck but panics instead of returning +// mustSanityCheck is like sanityCheck but panics instead of returning // a negative result. // -func MustSanityCheck(fn *Function, reporter io.Writer) { - if !SanityCheck(fn, reporter) { +func mustSanityCheck(fn *Function, reporter io.Writer) { + if !sanityCheck(fn, reporter) { panic("SanityCheck failed") } } @@ -344,3 +344,32 @@ func (s *sanity) checkFunction(fn *Function) bool { s.fn = nil return !s.insane } + +// sanityCheckPackage checks invariants of packages upon creation. +// It does not require that the package is built. +// Unlike sanityCheck (for functions), it just panics at the first error. +func sanityCheckPackage(pkg *Package) { + for name, mem := range pkg.Members { + if name != mem.Name() { + panic(fmt.Sprintf("%s: %T.Name() = %s, want %s", + pkg.Object.Path(), mem, mem.Name(), name)) + } + obj := mem.Object() + if obj == nil { + // This check is sound because fields + // {Global,Function}.object have type + // types.Object. (If they were declared as + // *types.{Var,Func}, we'd have a non-empty + // interface containing a nil pointer.) + + continue // not all members have typechecker objects + } + if obj.Name() != name { + panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s", + pkg.Object.Path(), mem, obj.Name(), name)) + } + if obj.Pos() != mem.Pos() { + panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos())) + } + } +} diff --git a/ssa/ssa.go b/ssa/ssa.go index 4bf78746..a9674a0c 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -55,11 +55,12 @@ type Package struct { // const, var, func and type declarations respectively. // type Member interface { - Name() string // the declared name of the package member - String() string // human-readable information about the value - Pos() token.Pos // position of member's declaration, if known - Type() types.Type // the type of the package member - Token() token.Token // token.{VAR,FUNC,CONST,TYPE} + Name() string // declared name of the package member + String() string // package-qualified name of the package member + Object() types.Object // typechecker's object for this member, if any + Pos() token.Pos // position of member's declaration, if known + Type() types.Type // type of the package member + Token() token.Token // token.{VAR,FUNC,CONST,TYPE} } // An Id identifies the name of a field of a struct type, or the name @@ -96,7 +97,7 @@ type MethodSet map[Id]*Function // Type() returns a *types.Named. // type Type struct { - Object *types.TypeName + object *types.TypeName } // A Constant is a Member of Package representing a package-level @@ -109,9 +110,9 @@ type Type struct { // it augments with the name and position of its 'const' declaration. // type Constant struct { - name string - Value *Literal - pos token.Pos + object *types.Const + Value *Literal + pos token.Pos } // An SSA value that can be referenced by an instruction. @@ -266,6 +267,7 @@ type Instruction interface { // type Function struct { name string + object types.Object // a *types.Func; may be nil for init, wrappers, etc. Signature *types.Signature pos token.Pos @@ -395,9 +397,10 @@ type Literal struct { // identifier. // type Global struct { - name string - typ types.Type - pos token.Pos + name string + object types.Object // a *types.Var; may be nil for synthetics e.g. init$guard + typ types.Type + pos token.Pos Pkg *Package @@ -1315,12 +1318,14 @@ func (v *Global) Name() string { return v.name } func (v *Global) Pos() token.Pos { return v.pos } func (*Global) Referrers() *[]Instruction { return nil } func (v *Global) Token() token.Token { return token.VAR } +func (v *Global) Object() types.Object { return v.object } func (v *Function) Name() string { return v.name } func (v *Function) Type() types.Type { return v.Signature } func (v *Function) Pos() token.Pos { return v.pos } func (*Function) Referrers() *[]Instruction { return nil } func (v *Function) Token() token.Token { return token.FUNC } +func (v *Function) Object() types.Object { return v.object } func (v *Parameter) Type() types.Type { return v.typ } func (v *Parameter) Name() string { return v.name } @@ -1346,17 +1351,23 @@ func (v *anInstruction) Parent() *Function { return v.block.parent } func (v *anInstruction) Block() *BasicBlock { return v.block } func (v *anInstruction) SetBlock(block *BasicBlock) { v.block = block } -func (t *Type) Name() string { return t.Object.Name() } -func (t *Type) Pos() token.Pos { return t.Object.Pos() } -func (t *Type) String() string { return t.Name() } -func (t *Type) Type() types.Type { return t.Object.Type() } -func (t *Type) Token() token.Token { return token.TYPE } +func (t *Type) Name() string { return t.object.Name() } +func (t *Type) Pos() token.Pos { return t.object.Pos() } +func (t *Type) Type() types.Type { return t.object.Type() } +func (t *Type) Token() token.Token { return token.TYPE } +func (t *Type) Object() types.Object { return t.object } +func (t *Type) String() string { + return fmt.Sprintf("%s.%s", t.object.Pkg().Path(), t.object.Name()) +} -func (c *Constant) Name() string { return c.name } -func (c *Constant) Pos() token.Pos { return c.pos } -func (c *Constant) String() string { return c.Name() } -func (c *Constant) Type() types.Type { return c.Value.Type() } -func (c *Constant) Token() token.Token { return token.CONST } +func (c *Constant) Name() string { return c.object.Name() } +func (c *Constant) Pos() token.Pos { return c.object.Pos() } +func (c *Constant) String() string { + return fmt.Sprintf("%s.%s", c.object.Pkg().Path(), c.object.Name()) +} +func (c *Constant) Type() types.Type { return c.object.Type() } +func (c *Constant) Token() token.Token { return token.CONST } +func (c *Constant) Object() types.Object { return c.object } // Func returns the package-level function of the specified name, // or nil if not found.