go.tools/ssa: two fixes to CallCommon.

(*CallCommon).Signature() now returns non-nil even for
built-ins.  Builtins are now created with specialized types for
each use.  Added sanity-check.

CallCommon.HasEllipsis field eliminated.  It was an incorrect
memoization of Signature().IsVariadic() used only for
printing.

Also: introduce and use newTypeVar utility.

R=gri
CC=golang-codereviews
https://golang.org/cl/46880044
This commit is contained in:
Alan Donovan 2014-01-07 13:31:05 -05:00
parent b6eef48120
commit d6eb8982f6
9 changed files with 68 additions and 56 deletions

View File

@ -49,8 +49,8 @@ type opaqueType struct {
func (t *opaqueType) String() string { return t.name } func (t *opaqueType) String() string { return t.name }
var ( var (
varOk = types.NewVar(token.NoPos, nil, "ok", tBool) varOk = newVar("ok", tBool)
varIndex = types.NewVar(token.NoPos, nil, "index", tInt) varIndex = newVar("index", tInt)
// Type constants. // Type constants.
tBool = types.Typ[types.Bool] tBool = types.Typ[types.Bool]
@ -236,7 +236,7 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value {
tuple.(interface { tuple.(interface {
setType(types.Type) setType(types.Type)
}).setType(types.NewTuple( }).setType(types.NewTuple(
types.NewVar(token.NoPos, nil, "value", typ), newVar("value", typ),
varOk, varOk,
)) ))
return tuple return tuple
@ -616,7 +616,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
// Universal built-in or nil? // Universal built-in or nil?
switch obj := obj.(type) { switch obj := obj.(type) {
case *types.Builtin: case *types.Builtin:
return fn.Prog.builtins[obj] return &Builtin{object: obj, sig: fn.Pkg.typeOf(e).(*types.Signature)}
case *types.Nil: case *types.Nil:
return nilConst(fn.Pkg.typeOf(e)) return nilConst(fn.Pkg.typeOf(e))
} }
@ -756,7 +756,6 @@ func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, se
// //
func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
c.pos = e.Lparen c.pos = e.Lparen
c.HasEllipsis = e.Ellipsis != 0
// Is this a method call? // Is this a method call?
if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok { if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
@ -1442,7 +1441,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
for _, st := range states { for _, st := range states {
if st.Dir == types.RecvOnly { if st.Dir == types.RecvOnly {
tElem := st.Chan.Type().Underlying().(*types.Chan).Elem() tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
vars = append(vars, types.NewVar(token.NoPos, nil, "", tElem)) vars = append(vars, newVar("", tElem))
} }
} }
sel.setType(types.NewTuple(vars...)) sel.setType(types.NewTuple(vars...))
@ -1610,7 +1609,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value
} else { } else {
// length = len(x). // length = len(x).
var c Call var c Call
c.Call.Value = fn.Prog.builtins[types.Universe.Lookup("len").(*types.Builtin)] c.Call.Value = makeLen(x.Type())
c.Call.Args = []Value{x} c.Call.Args = []Value{x}
c.setType(tInt) c.setType(tInt)
length = fn.emit(&c) length = fn.emit(&c)
@ -1714,8 +1713,8 @@ func (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.
} }
okv.setType(types.NewTuple( okv.setType(types.NewTuple(
varOk, varOk,
types.NewVar(token.NoPos, nil, "k", tk), newVar("k", tk),
types.NewVar(token.NoPos, nil, "v", tv), newVar("v", tv),
)) ))
fn.emit(okv) fn.emit(okv)
@ -1761,7 +1760,7 @@ func (b *builder) rangeChan(fn *Function, x Value, tk types.Type, pos token.Pos)
} }
recv.setPos(pos) recv.setPos(pos)
recv.setType(types.NewTuple( recv.setType(types.NewTuple(
types.NewVar(token.NoPos, nil, "k", x.Type().Underlying().(*types.Chan).Elem()), newVar("k", x.Type().Underlying().(*types.Chan).Elem()),
varOk, varOk,
)) ))
ko := fn.emit(recv) ko := fn.emit(recv)

View File

@ -39,24 +39,14 @@ const (
// mode controls diagnostics and checking during SSA construction. // mode controls diagnostics and checking during SSA construction.
// //
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
prog := &Program{ return &Program{
Fset: fset, Fset: fset,
imported: make(map[string]*Package), imported: make(map[string]*Package),
packages: make(map[*types.Package]*Package), packages: make(map[*types.Package]*Package),
builtins: make(map[*types.Builtin]*Builtin),
boundMethodWrappers: make(map[*types.Func]*Function), boundMethodWrappers: make(map[*types.Func]*Function),
ifaceMethodWrappers: make(map[*types.Func]*Function), ifaceMethodWrappers: make(map[*types.Func]*Function),
mode: mode, mode: mode,
} }
// Create Values for built-in functions.
for _, name := range types.Universe.Names() {
if obj, ok := types.Universe.Lookup(name).(*types.Builtin); ok {
prog.builtins[obj] = &Builtin{obj}
}
}
return prog
} }
// memberFromObject populates package pkg with a member for the // memberFromObject populates package pkg with a member for the

View File

@ -297,7 +297,7 @@ func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
} }
a.setPos(pos) a.setPos(pos)
a.setType(types.NewTuple( a.setType(types.NewTuple(
types.NewVar(token.NoPos, nil, "value", t), newVar("value", t),
varOk, varOk,
)) ))
return f.emit(a) return f.emit(a)

View File

@ -106,6 +106,6 @@ func main() {
// t2 = make interface{} <- string ("Hello, World!":string) interface{} // t2 = make interface{} <- string ("Hello, World!":string) interface{}
// *t1 = t2 // *t1 = t2
// t3 = slice t0[:] []interface{} // t3 = slice t0[:] []interface{}
// t4 = fmt.Println(t3) (n int, err error) // t4 = fmt.Println(t3...) (n int, err error)
// return // return
} }

View File

@ -120,7 +120,7 @@ func printCall(v *CallCommon, prefix string, instr Instruction) string {
} }
b.WriteString(relName(arg, instr)) b.WriteString(relName(arg, instr))
} }
if v.HasEllipsis { if v.Signature().IsVariadic() {
b.WriteString("...") b.WriteString("...")
} }
b.WriteString(")") b.WriteString(")")

View File

@ -216,7 +216,7 @@ func findMethod(prog *Program, meth *types.Selection) *Function {
func makeWrapper(prog *Program, typ types.Type, meth *types.Selection) *Function { func makeWrapper(prog *Program, typ types.Type, meth *types.Selection) *Function {
obj := meth.Obj().(*types.Func) obj := meth.Obj().(*types.Func)
oldsig := obj.Type().(*types.Signature) oldsig := obj.Type().(*types.Signature)
recv := types.NewVar(token.NoPos, nil, "recv", typ) recv := newVar("recv", typ)
description := fmt.Sprintf("wrapper for %s", obj) description := fmt.Sprintf("wrapper for %s", obj)
if prog.mode&LogSource != 0 { if prog.mode&LogSource != 0 {

View File

@ -179,6 +179,12 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
panic(fmt.Sprintf("Unknown instruction type: %T", instr)) panic(fmt.Sprintf("Unknown instruction type: %T", instr))
} }
if call, ok := instr.(CallInstruction); ok {
if call.Common().Signature() == nil {
s.errorf("nil signature: %s", call)
}
}
// Check that value-defining instructions have valid types. // Check that value-defining instructions have valid types.
if v, ok := instr.(Value); ok { if v, ok := instr.(Value); ok {
t := v.Type() t := v.Type()

View File

@ -25,7 +25,6 @@ type Program struct {
Fset *token.FileSet // position information for the files of this Program Fset *token.FileSet // position information for the files of this Program
imported map[string]*Package // all importable Packages, keyed by import path imported map[string]*Package // all importable Packages, keyed by import path
packages map[*types.Package]*Package // all loaded Packages, keyed by object packages map[*types.Package]*Package // all loaded Packages, keyed by object
builtins map[*types.Builtin]*Builtin // all built-in functions, keyed by typechecker objects.
mode BuilderMode // set of mode bits for SSA construction mode BuilderMode // set of mode bits for SSA construction
methodsMu sync.Mutex // guards the following maps: methodsMu sync.Mutex // guards the following maps:
@ -413,19 +412,19 @@ type Global struct {
Pkg *Package Pkg *Package
} }
// A Builtin represents a built-in function, e.g. len. // A Builtin represents a specific use of a built-in function, e.g. len.
// //
// Builtins are immutable values. Builtins do not have addresses. // Builtins are immutable values. Builtins do not have addresses.
// Builtins can only appear in CallCommon.Func. // Builtins can only appear in CallCommon.Func.
// //
// Object() returns a *types.Builtin. // Object() returns a *types.Builtin.
// //
// Type() returns types.Typ[types.Invalid], since built-in functions // Type() returns a *types.Signature representing the effective
// may have polymorphic or variadic types that are not expressible in // signature of the built-in for this call.
// Go's type system.
// //
type Builtin struct { type Builtin struct {
object *types.Builtin // canonical types.Universe object for this built-in object *types.Builtin // canonical types.Universe object for this built-in
sig *types.Signature
} }
// Value-defining instructions ---------------------------------------- // Value-defining instructions ----------------------------------------
@ -842,7 +841,7 @@ type SelectState struct {
DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode] DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]
} }
// The Select instruction tests whether (or blocks until) one or more // The Select instruction tests whether (or blocks until) one
// of the specified sent or received states is entered. // of the specified sent or received states is entered.
// //
// Let n be the number of States for which Dir==RECV and T_i (0<=i<n) // Let n be the number of States for which Dir==RECV and T_i (0<=i<n)
@ -874,7 +873,7 @@ type SelectState struct {
// Pos() returns the ast.SelectStmt.Select. // Pos() returns the ast.SelectStmt.Select.
// //
// Example printed form: // Example printed form:
// t3 = select nonblocking [<-t0, t1<-t2, ...] // t3 = select nonblocking [<-t0, t1<-t2]
// t4 = select blocking [] // t4 = select blocking []
// //
type Select struct { type Select struct {
@ -1239,13 +1238,18 @@ type anInstruction struct {
// which may be a *Builtin, a *Function or any other value of kind // which may be a *Builtin, a *Function or any other value of kind
// 'func'. // 'func'.
// //
// In the common case in which Value is a *Function, this indicates a // Value may be one of:
// statically dispatched call to a package-level function, an // (a) a *Function, indicating a statically dispatched call
// anonymous function, or a method of a named type. Also statically // to a package-level function, an anonymous function, or
// dispatched, but less common, Value may be a *MakeClosure, indicating // a method of a named type.
// an immediately applied function literal with free variables. Any // (b) a *MakeClosure, indicating an immediately applied
// other value of Value indicates a dynamically dispatched function // function literal with free variables.
// call. The StaticCallee method returns the callee in these cases. // (c) a *Builtin, indicating a statically dispatched call
// to a built-in function. StaticCallee returns nil.
// (d) any other value, indicating a dynamically dispatched
// function call.
// StaticCallee returns the identity of the callee in cases
// (a) and (b), nil otherwise.
// //
// Args contains the arguments to the call. If Value is a method, // Args contains the arguments to the call. If Value is a method,
// Args[0] contains the receiver parameter. // Args[0] contains the receiver parameter.
@ -1271,18 +1275,14 @@ type anInstruction struct {
// go invoke t3.Run(t2) // go invoke t3.Run(t2)
// defer invoke t4.Handle(...t5) // defer invoke t4.Handle(...t5)
// //
// In both modes, HasEllipsis is true iff the last element of Args is // For all calls to variadic functions (Signature().IsVariadic()),
// a slice value containing zero or more arguments to a variadic // the last element of Args is a slice.
// function. (This is not semantically significant since the type of
// the called function is sufficient to determine this, but it aids
// readability of the printed form.)
// //
type CallCommon struct { type CallCommon struct {
Value Value // receiver (invoke mode) or func value (call mode) Value Value // receiver (invoke mode) or func value (call mode)
Method *types.Func // abstract method (invoke mode) Method *types.Func // abstract method (invoke mode)
Args []Value // actual parameters (in static method call, includes receiver) Args []Value // actual parameters (in static method call, includes receiver)
HasEllipsis bool // true iff last Args is a slice of '...' args (needed?) pos token.Pos // position of CallExpr.Lparen, iff explicit in source
pos token.Pos // position of CallExpr.Lparen, iff explicit in source
} }
// IsInvoke returns true if this call has "invoke" (not "call") mode. // IsInvoke returns true if this call has "invoke" (not "call") mode.
@ -1300,18 +1300,15 @@ func (c *CallCommon) Pos() token.Pos { return c.pos }
// In either "call" or "invoke" mode, if the callee is a method, its // In either "call" or "invoke" mode, if the callee is a method, its
// receiver is represented by sig.Recv, not sig.Params().At(0). // receiver is represented by sig.Recv, not sig.Params().At(0).
// //
// Signature returns nil for a call to a built-in function.
//
func (c *CallCommon) Signature() *types.Signature { func (c *CallCommon) Signature() *types.Signature {
if c.Method != nil { if c.Method != nil {
return c.Method.Type().(*types.Signature) return c.Method.Type().(*types.Signature)
} }
sig, _ := c.Value.Type().Underlying().(*types.Signature) // nil for *Builtin return c.Value.Type().Underlying().(*types.Signature)
return sig
} }
// StaticCallee returns the called function if this is a trivially // StaticCallee returns the callee if this is a trivially static
// static "call"-mode call. // "call"-mode call to a function.
func (c *CallCommon) StaticCallee() *Function { func (c *CallCommon) StaticCallee() *Function {
switch fn := c.Value.(type) { switch fn := c.Value.(type) {
case *Function: case *Function:
@ -1360,7 +1357,7 @@ func (s *Call) Value() *Call { return s }
func (s *Defer) Value() *Call { return nil } func (s *Defer) Value() *Call { return nil }
func (s *Go) Value() *Call { return nil } func (s *Go) Value() *Call { return nil }
func (v *Builtin) Type() types.Type { return v.object.Type() } func (v *Builtin) Type() types.Type { return v.sig }
func (v *Builtin) Name() string { return v.object.Name() } func (v *Builtin) Name() string { return v.object.Name() }
func (*Builtin) Referrers() *[]Instruction { return nil } func (*Builtin) Referrers() *[]Instruction { return nil }
func (v *Builtin) Pos() token.Pos { return token.NoPos } func (v *Builtin) Pos() token.Pos { return token.NoPos }

View File

@ -9,6 +9,7 @@ package ssa
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token"
"io" "io"
"os" "os"
@ -117,3 +118,22 @@ func callsRecover(f *Function) bool {
} }
return false return false
} }
// newVar creates a 'var' for use in a types.Tuple.
func newVar(name string, typ types.Type) *types.Var {
return types.NewParam(token.NoPos, nil, name, typ)
}
var (
lenObject = types.Universe.Lookup("len").(*types.Builtin)
lenResults = types.NewTuple(newVar("", tInt))
)
// makeLen returns the len builtin specialized to type func(T)int.
func makeLen(T types.Type) *Builtin {
lenParams := types.NewTuple(newVar("", T))
return &Builtin{
object: lenObject,
sig: types.NewSignature(nil, nil, lenParams, lenResults, false),
}
}