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 }
|
||||
|
||||
var (
|
||||
varOk = types.NewVar(token.NoPos, nil, "ok", tBool)
|
||||
varIndex = types.NewVar(token.NoPos, nil, "index", tInt)
|
||||
varOk = newVar("ok", tBool)
|
||||
varIndex = newVar("index", tInt)
|
||||
|
||||
// Type constants.
|
||||
tBool = types.Typ[types.Bool]
|
||||
|
|
@ -236,7 +236,7 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value {
|
|||
tuple.(interface {
|
||||
setType(types.Type)
|
||||
}).setType(types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "value", typ),
|
||||
newVar("value", typ),
|
||||
varOk,
|
||||
))
|
||||
return tuple
|
||||
|
|
@ -616,7 +616,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
|
|||
// Universal built-in or nil?
|
||||
switch obj := obj.(type) {
|
||||
case *types.Builtin:
|
||||
return fn.Prog.builtins[obj]
|
||||
return &Builtin{object: obj, sig: fn.Pkg.typeOf(e).(*types.Signature)}
|
||||
case *types.Nil:
|
||||
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) {
|
||||
c.pos = e.Lparen
|
||||
c.HasEllipsis = e.Ellipsis != 0
|
||||
|
||||
// Is this a method call?
|
||||
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 {
|
||||
if st.Dir == types.RecvOnly {
|
||||
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...))
|
||||
|
|
@ -1610,7 +1609,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value
|
|||
} else {
|
||||
// length = len(x).
|
||||
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.setType(tInt)
|
||||
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(
|
||||
varOk,
|
||||
types.NewVar(token.NoPos, nil, "k", tk),
|
||||
types.NewVar(token.NoPos, nil, "v", tv),
|
||||
newVar("k", tk),
|
||||
newVar("v", tv),
|
||||
))
|
||||
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.setType(types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "k", x.Type().Underlying().(*types.Chan).Elem()),
|
||||
newVar("k", x.Type().Underlying().(*types.Chan).Elem()),
|
||||
varOk,
|
||||
))
|
||||
ko := fn.emit(recv)
|
||||
|
|
|
|||
|
|
@ -39,24 +39,14 @@ const (
|
|||
// mode controls diagnostics and checking during SSA construction.
|
||||
//
|
||||
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
|
||||
prog := &Program{
|
||||
return &Program{
|
||||
Fset: fset,
|
||||
imported: make(map[string]*Package),
|
||||
packages: make(map[*types.Package]*Package),
|
||||
builtins: make(map[*types.Builtin]*Builtin),
|
||||
boundMethodWrappers: make(map[*types.Func]*Function),
|
||||
ifaceMethodWrappers: make(map[*types.Func]*Function),
|
||||
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
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
|
|||
}
|
||||
a.setPos(pos)
|
||||
a.setType(types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "value", t),
|
||||
newVar("value", t),
|
||||
varOk,
|
||||
))
|
||||
return f.emit(a)
|
||||
|
|
|
|||
|
|
@ -106,6 +106,6 @@ func main() {
|
|||
// t2 = make interface{} <- string ("Hello, World!":string) interface{}
|
||||
// *t1 = t2
|
||||
// t3 = slice t0[:] []interface{}
|
||||
// t4 = fmt.Println(t3) (n int, err error)
|
||||
// t4 = fmt.Println(t3...) (n int, err error)
|
||||
// return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ func printCall(v *CallCommon, prefix string, instr Instruction) string {
|
|||
}
|
||||
b.WriteString(relName(arg, instr))
|
||||
}
|
||||
if v.HasEllipsis {
|
||||
if v.Signature().IsVariadic() {
|
||||
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 {
|
||||
obj := meth.Obj().(*types.Func)
|
||||
oldsig := obj.Type().(*types.Signature)
|
||||
recv := types.NewVar(token.NoPos, nil, "recv", typ)
|
||||
recv := newVar("recv", typ)
|
||||
|
||||
description := fmt.Sprintf("wrapper for %s", obj)
|
||||
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))
|
||||
}
|
||||
|
||||
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.
|
||||
if v, ok := instr.(Value); ok {
|
||||
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
|
||||
imported map[string]*Package // all importable Packages, keyed by import path
|
||||
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
|
||||
|
||||
methodsMu sync.Mutex // guards the following maps:
|
||||
|
|
@ -413,19 +412,19 @@ type Global struct {
|
|||
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 can only appear in CallCommon.Func.
|
||||
//
|
||||
// Object() returns a *types.Builtin.
|
||||
//
|
||||
// Type() returns types.Typ[types.Invalid], since built-in functions
|
||||
// may have polymorphic or variadic types that are not expressible in
|
||||
// Go's type system.
|
||||
// Type() returns a *types.Signature representing the effective
|
||||
// signature of the built-in for this call.
|
||||
//
|
||||
type Builtin struct {
|
||||
object *types.Builtin // canonical types.Universe object for this built-in
|
||||
sig *types.Signature
|
||||
}
|
||||
|
||||
// Value-defining instructions ----------------------------------------
|
||||
|
|
@ -842,7 +841,7 @@ type SelectState struct {
|
|||
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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Example printed form:
|
||||
// t3 = select nonblocking [<-t0, t1<-t2, ...]
|
||||
// t3 = select nonblocking [<-t0, t1<-t2]
|
||||
// t4 = select blocking []
|
||||
//
|
||||
type Select struct {
|
||||
|
|
@ -1239,13 +1238,18 @@ type anInstruction struct {
|
|||
// which may be a *Builtin, a *Function or any other value of kind
|
||||
// 'func'.
|
||||
//
|
||||
// In the common case in which Value is a *Function, this indicates a
|
||||
// statically dispatched call to a package-level function, an
|
||||
// anonymous function, or a method of a named type. Also statically
|
||||
// dispatched, but less common, Value may be a *MakeClosure, indicating
|
||||
// an immediately applied function literal with free variables. Any
|
||||
// other value of Value indicates a dynamically dispatched function
|
||||
// call. The StaticCallee method returns the callee in these cases.
|
||||
// Value may be one of:
|
||||
// (a) a *Function, indicating a statically dispatched call
|
||||
// to a package-level function, an anonymous function, or
|
||||
// a method of a named type.
|
||||
// (b) a *MakeClosure, indicating an immediately applied
|
||||
// function literal with free variables.
|
||||
// (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[0] contains the receiver parameter.
|
||||
|
|
@ -1271,18 +1275,14 @@ type anInstruction struct {
|
|||
// go invoke t3.Run(t2)
|
||||
// defer invoke t4.Handle(...t5)
|
||||
//
|
||||
// In both modes, HasEllipsis is true iff the last element of Args is
|
||||
// a slice value containing zero or more arguments to a variadic
|
||||
// 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.)
|
||||
// For all calls to variadic functions (Signature().IsVariadic()),
|
||||
// the last element of Args is a slice.
|
||||
//
|
||||
type CallCommon struct {
|
||||
Value Value // receiver (invoke mode) or func value (call mode)
|
||||
Method *types.Func // abstract method (invoke mode)
|
||||
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
|
||||
Value Value // receiver (invoke mode) or func value (call mode)
|
||||
Method *types.Func // abstract method (invoke mode)
|
||||
Args []Value // actual parameters (in static method call, includes receiver)
|
||||
pos token.Pos // position of CallExpr.Lparen, iff explicit in source
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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 {
|
||||
if c.Method != nil {
|
||||
return c.Method.Type().(*types.Signature)
|
||||
}
|
||||
sig, _ := c.Value.Type().Underlying().(*types.Signature) // nil for *Builtin
|
||||
return sig
|
||||
return c.Value.Type().Underlying().(*types.Signature)
|
||||
}
|
||||
|
||||
// StaticCallee returns the called function if this is a trivially
|
||||
// static "call"-mode call.
|
||||
// StaticCallee returns the callee if this is a trivially static
|
||||
// "call"-mode call to a function.
|
||||
func (c *CallCommon) StaticCallee() *Function {
|
||||
switch fn := c.Value.(type) {
|
||||
case *Function:
|
||||
|
|
@ -1360,7 +1357,7 @@ func (s *Call) Value() *Call { return s }
|
|||
func (s *Defer) 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 (*Builtin) Referrers() *[]Instruction { return nil }
|
||||
func (v *Builtin) Pos() token.Pos { return token.NoPos }
|
||||
|
|
|
|||
20
ssa/util.go
20
ssa/util.go
|
|
@ -9,6 +9,7 @@ package ssa
|
|||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
|
|
@ -117,3 +118,22 @@ func callsRecover(f *Function) bool {
|
|||
}
|
||||
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