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:
parent
b6eef48120
commit
d6eb8982f6
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(")")
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
59
ssa/ssa.go
59
ssa/ssa.go
|
|
@ -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 }
|
||||||
|
|
|
||||||
20
ssa/util.go
20
ssa/util.go
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue