go.tools/ssa: small changes accumulated during gri's vacation. :)
Method sets:
- Simplify CallCommon.
Avoid the implicit copy when calling a T method on a *T
receiver. This simplifies clients. Instead we generate
"indirection wrapper" functions that do this (like gc does).
New invariant:
m's receiver type is exactly T for all m in MethodSet(T)
- MakeInterface no longer holds the concrete type's MethodSet.
We can defer its computation this way.
- ssa.Type now just wraps a types.TypeName object.
MethodSets are computed as needed, not eagerly.
Position info:
- new CanonicalPos utility maps ast.Expr to canonical
token.Pos, as returned by {Instruction,Value}.Pos() methods.
- Don't set posn for implicit operations (e.g. varargs array alloc)
- Set position info for ChangeInterface and Slice instructions.
Cosmetic:
- add Member.Token() method
- simplify isPointer
- Omit words "interface", "slice" when printing MakeInterface,
MakeSlice; the type is enough.
- Comments on PathEnclosingInterval.
- Remove Function.FullName() where implicit String() suffices.
Also:
- Exposed NewLiteral to clients.
- Added ssa.Instruction.Parent() *Function
Added ssa.BasicBlock.Parent() *Function.
Added Sanity checks for above.
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/10166045
This commit is contained in:
parent
9ce6fcb502
commit
341a07a3aa
|
|
@ -62,8 +62,8 @@ var (
|
|||
// SSA Value constants.
|
||||
vZero = intLiteral(0)
|
||||
vOne = intLiteral(1)
|
||||
vTrue = newLiteral(exact.MakeBool(true), tBool)
|
||||
vFalse = newLiteral(exact.MakeBool(false), tBool)
|
||||
vTrue = NewLiteral(exact.MakeBool(true), tBool)
|
||||
vFalse = NewLiteral(exact.MakeBool(false), tBool)
|
||||
)
|
||||
|
||||
// builder holds state associated with the package currently being built.
|
||||
|
|
@ -230,7 +230,7 @@ func (b *builder) exprN(fn *Function, e ast.Expr) Value {
|
|||
tuple = fn.emit(lookup)
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e))
|
||||
return emitTypeTest(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e), e.Lparen)
|
||||
|
||||
case *ast.UnaryExpr: // must be receive <-
|
||||
typ = fn.Pkg.typeOf(e.X).Underlying().(*types.Chan).Elem()
|
||||
|
|
@ -345,7 +345,7 @@ func (b *builder) selector(fn *Function, e *ast.SelectorExpr, wantAddr, escaping
|
|||
if !wantAddr {
|
||||
if m, recv := b.findMethod(fn, e.X, id); m != nil {
|
||||
c := &MakeClosure{
|
||||
Fn: makeBoundMethodThunk(fn.Prog, m, recv),
|
||||
Fn: makeBoundMethodThunk(fn.Prog, m, recv.Type()),
|
||||
Bindings: []Value{recv},
|
||||
}
|
||||
c.setPos(e.Sel.Pos())
|
||||
|
|
@ -579,7 +579,7 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
|
|||
//
|
||||
func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
||||
if v := fn.Pkg.info.ValueOf(e); v != nil {
|
||||
return newLiteral(v, fn.Pkg.typeOf(e))
|
||||
return NewLiteral(v, fn.Pkg.typeOf(e))
|
||||
}
|
||||
|
||||
switch e := e.(type) {
|
||||
|
|
@ -618,7 +618,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
|||
return b.expr(fn, e.X)
|
||||
|
||||
case *ast.TypeAssertExpr: // single-result form only
|
||||
return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e))
|
||||
return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e), e.Lparen)
|
||||
|
||||
case *ast.CallExpr:
|
||||
typ := fn.Pkg.typeOf(e)
|
||||
|
|
@ -709,6 +709,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
|||
Low: low,
|
||||
High: high,
|
||||
}
|
||||
v.setPos(e.Lbrack)
|
||||
v.setType(fn.Pkg.typeOf(e))
|
||||
return fn.emit(v)
|
||||
|
||||
|
|
@ -969,7 +970,8 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx
|
|||
} else {
|
||||
// Replace a suffix of args with a slice containing it.
|
||||
at := types.NewArray(vt, int64(len(varargs)))
|
||||
a := emitNew(fn, at, e.Lparen)
|
||||
// Don't set pos (e.g. to e.Lparen) for implicit Allocs.
|
||||
a := emitNew(fn, at, token.NoPos)
|
||||
for i, arg := range varargs {
|
||||
iaddr := &IndexAddr{
|
||||
X: a,
|
||||
|
|
@ -1501,7 +1503,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
|||
if casetype == tUntypedNil {
|
||||
condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos)
|
||||
} else {
|
||||
yok := emitTypeTest(fn, x, casetype)
|
||||
yok := emitTypeTest(fn, x, casetype, token.NoPos)
|
||||
ti = emitExtract(fn, yok, 0, casetype)
|
||||
condv = emitExtract(fn, yok, 1, tBool)
|
||||
}
|
||||
|
|
@ -1643,7 +1645,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
|||
switch comm := clause.Comm.(type) {
|
||||
case *ast.AssignStmt: // x := <-states[state].Chan
|
||||
xdecl := fn.addNamedLocal(fn.Pkg.objectOf(comm.Lhs[0].(*ast.Ident)))
|
||||
recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref())
|
||||
recv := emitTypeAssert(fn, emitExtract(fn, triple, 1, tEface), xdecl.Type().Deref(), token.NoPos)
|
||||
emitStore(fn, xdecl, recv)
|
||||
|
||||
if len(comm.Lhs) == 2 { // x, ok := ...
|
||||
|
|
|
|||
|
|
@ -35,13 +35,14 @@ const (
|
|||
//
|
||||
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
|
||||
prog := &Program{
|
||||
Files: fset,
|
||||
Packages: make(map[string]*Package),
|
||||
packages: make(map[*types.Package]*Package),
|
||||
Builtins: make(map[types.Object]*Builtin),
|
||||
methodSets: make(map[types.Type]MethodSet),
|
||||
concreteMethods: make(map[*types.Func]*Function),
|
||||
mode: mode,
|
||||
Files: fset,
|
||||
Packages: make(map[string]*Package),
|
||||
packages: make(map[*types.Package]*Package),
|
||||
Builtins: make(map[types.Object]*Builtin),
|
||||
methodSets: make(map[types.Type]MethodSet),
|
||||
concreteMethods: make(map[*types.Func]*Function),
|
||||
indirectionWrappers: make(map[*Function]*Function),
|
||||
mode: mode,
|
||||
}
|
||||
|
||||
// Create Values for built-in functions.
|
||||
|
|
@ -67,25 +68,9 @@ func (prog *Program) CreatePackages(imp *importer.Importer) {
|
|||
// TODO(adonovan): make this idempotent, so that a second call
|
||||
// to CreatePackages creates only the packages that appeared
|
||||
// in imp since the first.
|
||||
//
|
||||
// TODO(adonovan): make it create packages in topological
|
||||
// order (using info.Imports()) so we can compute method sets
|
||||
// in postorder (or within createPackage) rather than as a
|
||||
// second pass.
|
||||
|
||||
for path, info := range imp.Packages {
|
||||
createPackage(prog, path, info)
|
||||
}
|
||||
|
||||
// Compute the method sets, now that we have all packages' methods.
|
||||
for _, pkg := range prog.Packages {
|
||||
for _, mem := range pkg.Members {
|
||||
if t, ok := mem.(*Type); ok {
|
||||
t.Methods = prog.MethodSet(t.NamedType)
|
||||
t.PtrMethods = prog.MethodSet(pointer(t.NamedType))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// memberFromObject populates package pkg with a member for the
|
||||
|
|
@ -99,12 +84,12 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
|||
name := obj.Name()
|
||||
switch obj := obj.(type) {
|
||||
case *types.TypeName:
|
||||
pkg.Members[name] = &Type{NamedType: obj.Type().(*types.Named)}
|
||||
pkg.Members[name] = &Type{Object: obj}
|
||||
|
||||
case *types.Const:
|
||||
pkg.Members[name] = &Constant{
|
||||
name: name,
|
||||
Value: newLiteral(obj.Val(), obj.Type()),
|
||||
Value: NewLiteral(obj.Val(), obj.Type()),
|
||||
pos: obj.Pos(),
|
||||
}
|
||||
|
||||
|
|
|
|||
16
ssa/emit.go
16
ssa/emit.go
|
|
@ -160,7 +160,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
|||
|
||||
// Assignment from one interface type to another?
|
||||
if _, ok := ut_src.(*types.Interface); ok {
|
||||
return emitTypeAssert(f, val, typ)
|
||||
return emitTypeAssert(f, val, typ, token.NoPos)
|
||||
}
|
||||
|
||||
// Untyped nil literal? Return interface-typed nil literal.
|
||||
|
|
@ -174,10 +174,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
|||
val = emitConv(f, val, DefaultType(ut_src))
|
||||
}
|
||||
|
||||
mi := &MakeInterface{
|
||||
X: val,
|
||||
Methods: f.Prog.MethodSet(t_src),
|
||||
}
|
||||
mi := &MakeInterface{X: val}
|
||||
mi.setType(typ)
|
||||
return f.emit(mi)
|
||||
}
|
||||
|
|
@ -188,7 +185,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
|||
// change yet; this defers the point at which the number of
|
||||
// possible representations explodes.
|
||||
if l, ok := val.(*Literal); ok {
|
||||
return newLiteral(l.Value, typ)
|
||||
return NewLiteral(l.Value, typ)
|
||||
}
|
||||
|
||||
// A representation-changing conversion.
|
||||
|
|
@ -246,7 +243,7 @@ func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value {
|
|||
// emitTypeAssert emits to f a type assertion value := x.(t) and
|
||||
// returns the value. x.Type() must be an interface.
|
||||
//
|
||||
func emitTypeAssert(f *Function, x Value, t types.Type) Value {
|
||||
func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
|
||||
// Simplify infallible assertions.
|
||||
txi := x.Type().Underlying().(*types.Interface)
|
||||
if ti, ok := t.Underlying().(*types.Interface); ok {
|
||||
|
|
@ -255,12 +252,14 @@ func emitTypeAssert(f *Function, x Value, t types.Type) Value {
|
|||
}
|
||||
if isSuperinterface(ti, txi) {
|
||||
c := &ChangeInterface{X: x}
|
||||
c.setPos(pos)
|
||||
c.setType(t)
|
||||
return f.emit(c)
|
||||
}
|
||||
}
|
||||
|
||||
a := &TypeAssert{X: x, AssertedType: t}
|
||||
a.setPos(pos)
|
||||
a.setType(t)
|
||||
return f.emit(a)
|
||||
}
|
||||
|
|
@ -268,7 +267,7 @@ func emitTypeAssert(f *Function, x Value, t types.Type) Value {
|
|||
// emitTypeTest emits to f a type test value,ok := x.(t) and returns
|
||||
// a (value, ok) tuple. x.Type() must be an interface.
|
||||
//
|
||||
func emitTypeTest(f *Function, x Value, t types.Type) Value {
|
||||
func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
|
||||
// TODO(adonovan): opt: simplify infallible tests as per
|
||||
// emitTypeAssert, and return (x, vTrue).
|
||||
// (Requires that exprN returns a slice of extracted values,
|
||||
|
|
@ -278,6 +277,7 @@ func emitTypeTest(f *Function, x Value, t types.Type) Value {
|
|||
AssertedType: t,
|
||||
CommaOk: true,
|
||||
}
|
||||
a.setPos(pos)
|
||||
a.setType(types.NewTuple(
|
||||
types.NewVar(token.NoPos, nil, "value", t),
|
||||
varOk,
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ func main() {
|
|||
// .0.entry: P:0 S:0
|
||||
// a0 = new [1]interface{} *[1]interface{}
|
||||
// t0 = &a0[0:untyped integer] *interface{}
|
||||
// t1 = make interface interface{} <- string ("Hello, World!":string) interface{}
|
||||
// t1 = make interface{} <- string ("Hello, World!":string) interface{}
|
||||
// *t0 = t1
|
||||
// t2 = slice a0[:] []interface{}
|
||||
// t3 = fmt.Println(t2) (n int, err error)
|
||||
|
|
|
|||
21
ssa/func.go
21
ssa/func.go
|
|
@ -19,6 +19,9 @@ func addEdge(from, to *BasicBlock) {
|
|||
to.Preds = append(to.Preds, from)
|
||||
}
|
||||
|
||||
// Parent returns the function that contains block b.
|
||||
func (b *BasicBlock) Parent() *Function { return b.parent }
|
||||
|
||||
// String returns a human-readable label of this block.
|
||||
// It is not guaranteed unique within the function.
|
||||
//
|
||||
|
|
@ -168,9 +171,10 @@ func (f *Function) labelledBlock(label *ast.Ident) *lblock {
|
|||
//
|
||||
func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter {
|
||||
v := &Parameter{
|
||||
name: name,
|
||||
typ: typ,
|
||||
pos: pos,
|
||||
name: name,
|
||||
typ: typ,
|
||||
pos: pos,
|
||||
parent: f,
|
||||
}
|
||||
f.Params = append(f.Params, v)
|
||||
return v
|
||||
|
|
@ -412,10 +416,11 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value {
|
|||
}
|
||||
outer := f.Enclosing.lookup(obj, true) // escaping
|
||||
v := &Capture{
|
||||
name: outer.Name(),
|
||||
typ: outer.Type(),
|
||||
pos: outer.Pos(),
|
||||
outer: outer,
|
||||
name: outer.Name(),
|
||||
typ: outer.Type(),
|
||||
pos: outer.Pos(),
|
||||
outer: outer,
|
||||
parent: f,
|
||||
}
|
||||
f.objects[obj] = v
|
||||
f.FreeVars = append(f.FreeVars, v)
|
||||
|
|
@ -612,7 +617,7 @@ func (f *Function) newBasicBlock(comment string) *BasicBlock {
|
|||
b := &BasicBlock{
|
||||
Index: len(f.Blocks),
|
||||
Comment: comment,
|
||||
Func: f,
|
||||
parent: f,
|
||||
}
|
||||
b.Succs = b.succs2[:0]
|
||||
f.Blocks = append(f.Blocks, b)
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
|
|||
}
|
||||
|
||||
// prepareCall determines the function value and argument values for a
|
||||
// function call in a Call, Go or Defer instruction, peforming
|
||||
// function call in a Call, Go or Defer instruction, performing
|
||||
// interface method lookup if needed.
|
||||
//
|
||||
func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
|
||||
|
|
@ -383,23 +383,12 @@ func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) {
|
|||
panic("method invoked on nil interface")
|
||||
}
|
||||
id := call.MethodId()
|
||||
m := findMethodSet(fr.i, recv.t)[id]
|
||||
if m == nil {
|
||||
fn = findMethodSet(fr.i, recv.t)[id]
|
||||
if fn == nil {
|
||||
// Unreachable in well-typed programs.
|
||||
panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, id))
|
||||
}
|
||||
_, aptr := recv.v.(*value) // actual pointerness
|
||||
_, fptr := m.Signature.Recv().Type().(*types.Pointer) // formal pointerness
|
||||
switch {
|
||||
case aptr == fptr:
|
||||
args = append(args, copyVal(recv.v))
|
||||
case aptr:
|
||||
// Calling func(T) with a *T receiver: make a copy.
|
||||
args = append(args, copyVal(*recv.v.(*value)))
|
||||
case fptr:
|
||||
panic("illegal call of *T method with T receiver")
|
||||
}
|
||||
fn = m
|
||||
args = append(args, copyVal(recv.v))
|
||||
}
|
||||
for _, arg := range call.Args {
|
||||
args = append(args, fr.get(arg))
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
|
|||
fmt.Fprintln(os.Stderr, "liftAlloc: lifting ", alloc, alloc.Name())
|
||||
}
|
||||
|
||||
fn := alloc.Block().Func
|
||||
fn := alloc.Parent()
|
||||
|
||||
// Φ-insertion.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -11,25 +11,23 @@ import (
|
|||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// newLiteral returns a new literal of the specified value and type.
|
||||
// NewLiteral returns a new literal of the specified value and type.
|
||||
// val must be valid according to the specification of Literal.Value.
|
||||
//
|
||||
func newLiteral(val exact.Value, typ types.Type) *Literal {
|
||||
// This constructor exists to provide a single place to
|
||||
// insert logging/assertions during debugging.
|
||||
func NewLiteral(val exact.Value, typ types.Type) *Literal {
|
||||
return &Literal{typ, val}
|
||||
}
|
||||
|
||||
// intLiteral returns an untyped integer literal that evaluates to i.
|
||||
func intLiteral(i int64) *Literal {
|
||||
return newLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt])
|
||||
return NewLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt])
|
||||
}
|
||||
|
||||
// nilLiteral returns a nil literal of the specified type, which may
|
||||
// be any reference type, including interfaces.
|
||||
//
|
||||
func nilLiteral(typ types.Type) *Literal {
|
||||
return newLiteral(exact.MakeNil(), typ)
|
||||
return NewLiteral(exact.MakeNil(), typ)
|
||||
}
|
||||
|
||||
// zeroLiteral returns a new "zero" literal of the specified type,
|
||||
|
|
@ -41,11 +39,11 @@ func zeroLiteral(t types.Type) *Literal {
|
|||
case *types.Basic:
|
||||
switch {
|
||||
case t.Info()&types.IsBoolean != 0:
|
||||
return newLiteral(exact.MakeBool(false), t)
|
||||
return NewLiteral(exact.MakeBool(false), t)
|
||||
case t.Info()&types.IsNumeric != 0:
|
||||
return newLiteral(exact.MakeInt64(0), t)
|
||||
return NewLiteral(exact.MakeInt64(0), t)
|
||||
case t.Info()&types.IsString != 0:
|
||||
return newLiteral(exact.MakeString(""), t)
|
||||
return NewLiteral(exact.MakeString(""), t)
|
||||
case t.Kind() == types.UnsafePointer:
|
||||
fallthrough
|
||||
case t.Kind() == types.UntypedNil:
|
||||
|
|
@ -56,7 +54,7 @@ func zeroLiteral(t types.Type) *Literal {
|
|||
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
|
||||
return nilLiteral(t)
|
||||
case *types.Named:
|
||||
return newLiteral(zeroLiteral(t.Underlying()).Value, t)
|
||||
return NewLiteral(zeroLiteral(t.Underlying()).Value, t)
|
||||
case *types.Array, *types.Struct:
|
||||
panic(fmt.Sprint("zeroLiteral applied to aggregate:", t))
|
||||
}
|
||||
|
|
|
|||
23
ssa/print.go
23
ssa/print.go
|
|
@ -28,14 +28,14 @@ func (id Id) String() string {
|
|||
func relName(v Value, i Instruction) string {
|
||||
switch v := v.(type) {
|
||||
case *Global:
|
||||
if i != nil && v.Pkg == i.Block().Func.Pkg {
|
||||
if i != nil && v.Pkg == i.Parent().Pkg {
|
||||
return v.Name()
|
||||
}
|
||||
return v.FullName()
|
||||
case *Function:
|
||||
var pkg *Package
|
||||
if i != nil {
|
||||
pkg = i.Block().Func.Pkg
|
||||
pkg = i.Parent().Pkg
|
||||
}
|
||||
return v.fullName(pkg)
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ func (v *ChangeInterface) String() string {
|
|||
}
|
||||
|
||||
func (v *MakeInterface) String() string {
|
||||
return fmt.Sprintf("make interface %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v))
|
||||
return fmt.Sprintf("make %s <- %s (%s)", v.Type(), v.X.Type(), relName(v.X, v))
|
||||
}
|
||||
|
||||
func (v *MakeClosure) String() string {
|
||||
|
|
@ -187,7 +187,7 @@ func (v *MakeClosure) String() string {
|
|||
|
||||
func (v *MakeSlice) String() string {
|
||||
var b bytes.Buffer
|
||||
b.WriteString("make slice ")
|
||||
b.WriteString("make ")
|
||||
b.WriteString(v.Type().String())
|
||||
b.WriteString(" ")
|
||||
b.WriteString(relName(v.Len, v))
|
||||
|
|
@ -381,19 +381,22 @@ func (p *Package) DumpTo(w io.Writer) {
|
|||
fmt.Fprintf(w, " func %-*s %s\n", maxname, name, mem.Type())
|
||||
|
||||
case *Type:
|
||||
fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.NamedType.Underlying())
|
||||
// We display only PtrMethods since its keys
|
||||
// are a superset of Methods' keys, though the
|
||||
fmt.Fprintf(w, " type %-*s %s\n", maxname, name, mem.Type().Underlying())
|
||||
// We display only mset(*T) since its keys
|
||||
// are a superset of mset(T)'s keys, though the
|
||||
// methods themselves may differ,
|
||||
// e.g. different bridge methods.
|
||||
// TODO(adonovan): show pointerness of receivers.
|
||||
// NB: if mem.Type() is a pointer, mset is empty.
|
||||
mset := p.Prog.MethodSet(pointer(mem.Type()))
|
||||
var keys ids
|
||||
for id := range mem.PtrMethods {
|
||||
for id := range mset {
|
||||
keys = append(keys, id)
|
||||
}
|
||||
sort.Sort(keys)
|
||||
for _, id := range keys {
|
||||
method := mem.PtrMethods[id]
|
||||
method := mset[id]
|
||||
// TODO(adonovan): show pointerness of receiver of declared method, not the index
|
||||
|
||||
fmt.Fprintf(w, " method %s %s\n", id, method.Signature)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,8 @@ func (c candidate) ptrRecv() bool {
|
|||
}
|
||||
|
||||
// MethodSet returns the method set for type typ,
|
||||
// building bridge methods as needed for promoted methods.
|
||||
// building bridge methods as needed for promoted methods
|
||||
// and indirection wrappers for *T receiver types.
|
||||
// A nil result indicates an empty set.
|
||||
//
|
||||
// Thread-safe.
|
||||
|
|
@ -104,7 +105,7 @@ func (p *Program) MethodSet(typ types.Type) MethodSet {
|
|||
defer p.methodSetsMu.Unlock()
|
||||
|
||||
// TODO(adonovan): Using Types as map keys doesn't properly
|
||||
// de-dup. e.g. *NamedType are canonical but *Struct and
|
||||
// de-dup. e.g. *Named are canonical but *Struct and
|
||||
// others are not. Need to de-dup using typemap.T.
|
||||
mset := p.methodSets[typ]
|
||||
if mset == nil {
|
||||
|
|
@ -151,7 +152,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
|||
m := nt.Method(i)
|
||||
concrete := prog.concreteMethods[m]
|
||||
if concrete == nil {
|
||||
panic(fmt.Sprintf("no ssa.Function for mset(%s)[%s]", t, m.Name()))
|
||||
panic(fmt.Sprintf("no ssa.Function for methodset(%s)[%s]", t, m.Name()))
|
||||
}
|
||||
addCandidate(nextcands, MakeId(m.Name(), m.Pkg()), m, concrete, node)
|
||||
}
|
||||
|
|
@ -205,7 +206,7 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
|||
if cand == nil {
|
||||
continue // blocked; ignore
|
||||
}
|
||||
if cand.ptrRecv() && !(isPointer(typ) || cand.path.isIndirect()) {
|
||||
if cand.ptrRecv() && !isPointer(typ) && !cand.path.isIndirect() {
|
||||
// A candidate concrete method f with receiver
|
||||
// *C is promoted into the method set of
|
||||
// (non-pointer) E iff the implicit path selection
|
||||
|
|
@ -214,8 +215,13 @@ func buildMethodSet(prog *Program, typ types.Type) MethodSet {
|
|||
}
|
||||
var method *Function
|
||||
if cand.path == nil {
|
||||
// Trivial member of method-set; no bridge needed.
|
||||
// Trivial member of method-set; no promotion needed.
|
||||
method = cand.concrete
|
||||
|
||||
if !cand.ptrRecv() && isPointer(typ) {
|
||||
// Call to method on T from receiver of type *T.
|
||||
method = indirectionWrapper(method)
|
||||
}
|
||||
} else {
|
||||
method = makeBridgeMethod(prog, typ, cand)
|
||||
}
|
||||
|
|
@ -332,7 +338,9 @@ func makeBridgeMethod(prog *Program, typ types.Type, cand *candidate) *Function
|
|||
return fn
|
||||
}
|
||||
|
||||
// createParams creates parameters for bridge method fn based on its Signature.
|
||||
// createParams creates parameters for bridge method fn based on its
|
||||
// Signature.Params, which do not include the receiver.
|
||||
//
|
||||
func createParams(fn *Function) {
|
||||
var last *Parameter
|
||||
tparams := fn.Signature.Params()
|
||||
|
|
@ -412,7 +420,7 @@ func makeImethodThunk(prog *Program, typ types.Type, id Id) *Function {
|
|||
//
|
||||
// TODO(adonovan): memoize creation of these functions in the Program.
|
||||
//
|
||||
func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function {
|
||||
func makeBoundMethodThunk(prog *Program, meth *Function, recvType types.Type) *Function {
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("makeBoundMethodThunk %s", meth)()
|
||||
}
|
||||
|
|
@ -423,7 +431,7 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function {
|
|||
Prog: prog,
|
||||
}
|
||||
|
||||
cap := &Capture{name: "recv", typ: recv.Type()}
|
||||
cap := &Capture{name: "recv", typ: recvType, parent: fn}
|
||||
fn.FreeVars = []*Capture{cap}
|
||||
fn.startBody()
|
||||
createParams(fn)
|
||||
|
|
@ -438,6 +446,53 @@ func makeBoundMethodThunk(prog *Program, meth *Function, recv Value) *Function {
|
|||
return fn
|
||||
}
|
||||
|
||||
// Receiver indirection wrapper ------------------------------------
|
||||
|
||||
// indirectionWrapper returns a synthetic method with *T receiver
|
||||
// that delegates to meth, which has a T receiver.
|
||||
//
|
||||
// func (recv *T) f(...) ... {
|
||||
// return (*recv).f(...)
|
||||
// }
|
||||
//
|
||||
// EXCLUSIVE_LOCKS_REQUIRED(meth.Prog.methodSetsMu)
|
||||
//
|
||||
func indirectionWrapper(meth *Function) *Function {
|
||||
prog := meth.Prog
|
||||
fn, ok := prog.indirectionWrappers[meth]
|
||||
if !ok {
|
||||
if prog.mode&LogSource != 0 {
|
||||
defer logStack("makeIndirectionWrapper %s", meth)()
|
||||
}
|
||||
|
||||
s := meth.Signature
|
||||
recv := types.NewVar(token.NoPos, meth.Pkg.Types, "recv",
|
||||
types.NewPointer(s.Recv().Type()))
|
||||
fn = &Function{
|
||||
name: meth.Name(),
|
||||
Signature: types.NewSignature(recv, s.Params(), s.Results(), s.IsVariadic()),
|
||||
Prog: prog,
|
||||
}
|
||||
|
||||
fn.startBody()
|
||||
fn.addParamObj(recv)
|
||||
createParams(fn)
|
||||
// TODO(adonovan): consider emitting a nil-pointer check here
|
||||
// with a nice error message, like gc does.
|
||||
var c Call
|
||||
c.Call.Func = meth
|
||||
c.Call.Args = append(c.Call.Args, emitLoad(fn, fn.Params[0]))
|
||||
for _, arg := range fn.Params[1:] {
|
||||
c.Call.Args = append(c.Call.Args, arg)
|
||||
}
|
||||
emitTailCall(fn, &c)
|
||||
fn.finishBody()
|
||||
|
||||
prog.indirectionWrappers[meth] = fn
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
// Implicit field promotion ----------------------------------------
|
||||
|
||||
// For a given struct type and (promoted) field Id, findEmbeddedField
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func MustSanityCheck(fn *Function, reporter io.Writer) {
|
|||
}
|
||||
|
||||
func (s *sanity) diagnostic(prefix, format string, args ...interface{}) {
|
||||
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn.FullName())
|
||||
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
|
||||
if s.block != nil {
|
||||
fmt.Fprintf(s.reporter, ", block %s", s.block)
|
||||
}
|
||||
|
|
@ -197,8 +197,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
|||
if b.Index != index {
|
||||
s.errorf("block has incorrect Index %d", b.Index)
|
||||
}
|
||||
if b.Func != s.fn {
|
||||
s.errorf("block has incorrect Func %s", b.Func.FullName())
|
||||
if b.parent != s.fn {
|
||||
s.errorf("block has incorrect parent %s", b.parent)
|
||||
}
|
||||
|
||||
// Check all blocks are reachable.
|
||||
|
|
@ -226,8 +226,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
|||
if !found {
|
||||
s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
|
||||
}
|
||||
if a.Func != s.fn {
|
||||
s.errorf("predecessor %s belongs to different function %s", a, a.Func.FullName())
|
||||
if a.parent != s.fn {
|
||||
s.errorf("predecessor %s belongs to different function %s", a, a.parent)
|
||||
}
|
||||
}
|
||||
for _, c := range b.Succs {
|
||||
|
|
@ -241,8 +241,8 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) {
|
|||
if !found {
|
||||
s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
|
||||
}
|
||||
if c.Func != s.fn {
|
||||
s.errorf("successor %s belongs to different function %s", c, c.Func.FullName())
|
||||
if c.parent != s.fn {
|
||||
s.errorf("successor %s belongs to different function %s", c, c.parent)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,10 +286,24 @@ func (s *sanity) checkFunction(fn *Function) bool {
|
|||
s.errorf("nil Prog")
|
||||
}
|
||||
for i, l := range fn.Locals {
|
||||
if l.Parent() != fn {
|
||||
s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
|
||||
}
|
||||
if l.Heap {
|
||||
s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
|
||||
}
|
||||
}
|
||||
for i, p := range fn.Params {
|
||||
if p.Parent() != fn {
|
||||
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
|
||||
}
|
||||
}
|
||||
for i, fv := range fn.FreeVars {
|
||||
if fv.Parent() != fn {
|
||||
s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
|
||||
}
|
||||
}
|
||||
|
||||
if fn.Blocks != nil && len(fn.Blocks) == 0 {
|
||||
// Function _had_ blocks (so it's not external) but
|
||||
// they were "optimized" away, even the entry block.
|
||||
|
|
|
|||
|
|
@ -26,17 +26,19 @@ func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
|
|||
//
|
||||
// imp provides ASTs for the program's packages.
|
||||
//
|
||||
// pkg may be nil if no SSA package has yet been created for the found
|
||||
// package. Call prog.CreatePackages(imp) to avoid this.
|
||||
//
|
||||
// The result is (nil, nil, false) if not found.
|
||||
//
|
||||
func (prog *Program) PathEnclosingInterval(imp *importer.Importer, start, end token.Pos) (pkg *Package, path []ast.Node, exact bool) {
|
||||
for path, info := range imp.Packages {
|
||||
pkg := prog.Packages[path]
|
||||
for importPath, info := range imp.Packages {
|
||||
for _, f := range info.Files {
|
||||
if !tokenFileContainsPos(prog.Files.File(f.Package), start) {
|
||||
continue
|
||||
}
|
||||
if path, exact := PathEnclosingInterval(f, start, end); path != nil {
|
||||
return pkg, path, exact
|
||||
return prog.Packages[importPath], path, exact
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,7 +136,12 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
|
|||
return mem
|
||||
}
|
||||
case *Type:
|
||||
for _, meth := range mem.PtrMethods {
|
||||
for _, meth := range pkg.Prog.MethodSet(mem.Type()) {
|
||||
if meth.Pos() == pos {
|
||||
return meth
|
||||
}
|
||||
}
|
||||
for _, meth := range pkg.Prog.MethodSet(pointer(mem.Type())) {
|
||||
if meth.Pos() == pos {
|
||||
return meth
|
||||
}
|
||||
|
|
@ -143,3 +150,77 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanonicalPos returns the canonical position of the AST node n,
|
||||
//
|
||||
// For each Node kind that may generate an SSA Value or Instruction,
|
||||
// exactly one token within it is designated as "canonical". The
|
||||
// position of that token is returned by {Value,Instruction}.Pos().
|
||||
// The specifications of those methods determine the implementation of
|
||||
// this function.
|
||||
//
|
||||
// TODO(adonovan): test coverage.
|
||||
//
|
||||
func CanonicalPos(n ast.Node) token.Pos {
|
||||
// Comments show the Value/Instruction kinds v that may be
|
||||
// created by n such that CanonicalPos(n) == v.Pos().
|
||||
switch n := n.(type) {
|
||||
case *ast.ParenExpr:
|
||||
return CanonicalPos(n.X)
|
||||
|
||||
case *ast.CallExpr:
|
||||
// f(x): *Call, *Go, *Defer.
|
||||
// T(x): *ChangeType, *Convert, *MakeInterface, *ChangeInterface.
|
||||
// make(): *MakeMap, *MakeChan, *MakeSlice.
|
||||
// new(): *Alloc.
|
||||
// panic(): *Panic.
|
||||
return n.Lparen
|
||||
|
||||
case *ast.Ident:
|
||||
return n.NamePos // *Parameter, *Alloc, *Capture
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
return n.Lparen // *ChangeInterface or *TypeAssertExpr
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
return n.Sel.NamePos // *MakeClosure, *Field or *FieldAddr
|
||||
|
||||
case *ast.FuncLit:
|
||||
return n.Type.Func // *Function or *MakeClosure
|
||||
|
||||
case *ast.CompositeLit:
|
||||
return n.Lbrace // *Alloc or *Slice
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
return n.OpPos // *Phi or *BinOp
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
return n.OpPos // *Phi or *UnOp
|
||||
|
||||
case *ast.IndexExpr:
|
||||
return n.Lbrack // *Index or *IndexAddr
|
||||
|
||||
case *ast.SliceExpr:
|
||||
return n.Lbrack // *Slice
|
||||
|
||||
case *ast.SelectStmt:
|
||||
return n.Select // *Select
|
||||
|
||||
case *ast.RangeStmt:
|
||||
return n.For // *Range
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
return n.Return // *Ret
|
||||
|
||||
case *ast.SendStmt:
|
||||
return n.Arrow // *Send
|
||||
|
||||
case *ast.StarExpr:
|
||||
return n.Star // *Store
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
return n.Colon // *MapUpdate
|
||||
}
|
||||
|
||||
return token.NoPos
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
// additional whitespace abutting a node to be enclosed by it.
|
||||
// In this example:
|
||||
//
|
||||
// z := x + y // add them
|
||||
// z := x + y // add them
|
||||
// <-A->
|
||||
// <----B----->
|
||||
//
|
||||
|
|
@ -41,7 +41,7 @@ import (
|
|||
// interior whitespace of path[0].
|
||||
// In this example:
|
||||
//
|
||||
// z := x + y // add them
|
||||
// z := x + y // add them
|
||||
// <--C--> <---E-->
|
||||
// ^
|
||||
// D
|
||||
|
|
@ -430,7 +430,10 @@ func childrenOf(n ast.Node) []ast.Node {
|
|||
children = append(children, tok(n.Switch, len("switch")))
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
// nop
|
||||
children = append(children,
|
||||
tok(n.Lparen-1, len(".")),
|
||||
tok(n.Lparen, len("(")),
|
||||
tok(n.Rparen, len(")")))
|
||||
|
||||
case *ast.TypeSpec:
|
||||
// TODO(adonovan): TypeSpec.{Doc,Comment}?
|
||||
|
|
|
|||
123
ssa/ssa.go
123
ssa/ssa.go
|
|
@ -17,15 +17,16 @@ import (
|
|||
// A Program is a partial or complete Go program converted to SSA form.
|
||||
//
|
||||
type Program struct {
|
||||
Files *token.FileSet // position information for the files of this Program [TODO: rename Fset]
|
||||
Packages map[string]*Package // all loaded Packages, keyed by import path [TODO rename packagesByPath]
|
||||
packages map[*types.Package]*Package // all loaded Packages, keyed by object [TODO rename Packages]
|
||||
Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
|
||||
Files *token.FileSet // position information for the files of this Program [TODO: rename Fset]
|
||||
Packages map[string]*Package // all loaded Packages, keyed by import path [TODO rename packagesByPath]
|
||||
packages map[*types.Package]*Package // all loaded Packages, keyed by object [TODO rename Packages]
|
||||
Builtins map[types.Object]*Builtin // all built-in functions, keyed by typechecker objects.
|
||||
concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
|
||||
mode BuilderMode // set of mode bits for SSA construction
|
||||
|
||||
methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
|
||||
methodSetsMu sync.Mutex // serializes all accesses to methodSets
|
||||
concreteMethods map[*types.Func]*Function // maps named concrete methods to their code
|
||||
mode BuilderMode // set of mode bits for SSA construction
|
||||
methodSetsMu sync.Mutex // guards methodSets, indirectionWrappers
|
||||
methodSets map[types.Type]MethodSet // concrete method sets for all needed types [TODO(adonovan): de-dup]
|
||||
indirectionWrappers map[*Function]*Function // func(*T) wrappers for T-methods
|
||||
}
|
||||
|
||||
// A Package is a single analyzed Go package containing Members for
|
||||
|
|
@ -51,10 +52,11 @@ 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
|
||||
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}
|
||||
}
|
||||
|
||||
// An Id identifies the name of a field of a struct type, or the name
|
||||
|
|
@ -77,20 +79,21 @@ type Id struct {
|
|||
Name string
|
||||
}
|
||||
|
||||
// A MethodSet contains all the methods for a particular type.
|
||||
// A MethodSet contains all the methods for a particular type T.
|
||||
// The method sets for T and *T are distinct entities.
|
||||
// The methods for a non-pointer type T all have receiver type T, but
|
||||
// the methods for pointer type *T may have receiver type *T or T.
|
||||
//
|
||||
// All methods in the method set for T have a receiver type of exactly
|
||||
// T. The method set of *T may contain synthetic indirection methods
|
||||
// that wrap methods whose receiver type is T.
|
||||
//
|
||||
type MethodSet map[Id]*Function
|
||||
|
||||
// A Type is a Member of a Package representing the name, underlying
|
||||
// type and method set of a named type declared at package scope.
|
||||
// A Type is a Member of a Package representing a package-level named type.
|
||||
//
|
||||
// Type() returns a *types.Named.
|
||||
//
|
||||
type Type struct {
|
||||
NamedType *types.Named
|
||||
Methods MethodSet // concrete method set of N
|
||||
PtrMethods MethodSet // concrete method set of (*N)
|
||||
Object *types.TypeName
|
||||
}
|
||||
|
||||
// A Constant is a Member of Package representing a package-level
|
||||
|
|
@ -190,6 +193,10 @@ type Instruction interface {
|
|||
// memory 'local int' and a definition of a pointer y.)
|
||||
String() string
|
||||
|
||||
// Parent returns the function to which this instruction
|
||||
// belongs.
|
||||
Parent() *Function
|
||||
|
||||
// Block returns the basic block to which this instruction
|
||||
// belongs.
|
||||
Block() *BasicBlock
|
||||
|
|
@ -299,7 +306,7 @@ type Function struct {
|
|||
type BasicBlock struct {
|
||||
Index int // index of this block within Func.Blocks
|
||||
Comment string // optional label; no semantic significance
|
||||
Func *Function // containing function
|
||||
parent *Function // parent function
|
||||
Instrs []Instruction // instructions in order
|
||||
Preds, Succs []*BasicBlock // predecessors and successors
|
||||
succs2 [2]*BasicBlock // initial space for Succs.
|
||||
|
|
@ -327,10 +334,10 @@ type BasicBlock struct {
|
|||
// belongs to an enclosing function.
|
||||
//
|
||||
type Capture struct {
|
||||
name string
|
||||
typ types.Type
|
||||
pos token.Pos
|
||||
|
||||
name string
|
||||
typ types.Type
|
||||
pos token.Pos
|
||||
parent *Function
|
||||
referrers []Instruction
|
||||
|
||||
// Transiently needed during building.
|
||||
|
|
@ -340,10 +347,10 @@ type Capture struct {
|
|||
// A Parameter represents an input parameter of a function.
|
||||
//
|
||||
type Parameter struct {
|
||||
name string
|
||||
typ types.Type
|
||||
pos token.Pos
|
||||
|
||||
name string
|
||||
typ types.Type
|
||||
pos token.Pos
|
||||
parent *Function
|
||||
referrers []Instruction
|
||||
}
|
||||
|
||||
|
|
@ -584,8 +591,12 @@ type Convert struct {
|
|||
// This operation cannot fail. Use TypeAssert for interface
|
||||
// conversions that may fail dynamically.
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
|
||||
// from an explicit conversion in the source.
|
||||
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
|
||||
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
|
||||
// instruction arose from an explicit e.(T) operation; or token.NoPos
|
||||
// otherwise.
|
||||
//
|
||||
// TODO(adonovan) what about the nil case of e = e.(E)? Test.
|
||||
//
|
||||
// Example printed form:
|
||||
// t1 = change interface interface{} <- I (t0)
|
||||
|
|
@ -596,7 +607,9 @@ type ChangeInterface struct {
|
|||
}
|
||||
|
||||
// MakeInterface constructs an instance of an interface type from a
|
||||
// value and its method-set.
|
||||
// value of a concrete type.
|
||||
//
|
||||
// Use Program.MethodSet(X.Type()) to find the method-set of X.
|
||||
//
|
||||
// To construct the zero value of an interface type T, use:
|
||||
// &Literal{exact.MakeNil(), T}
|
||||
|
|
@ -605,12 +618,12 @@ type ChangeInterface struct {
|
|||
// from an explicit conversion in the source.
|
||||
//
|
||||
// Example printed form:
|
||||
// t1 = make interface interface{} <- int (42:int)
|
||||
// t1 = make interface{} <- int (42:int)
|
||||
// t2 = make Stringer <- t0
|
||||
//
|
||||
type MakeInterface struct {
|
||||
Register
|
||||
X Value
|
||||
Methods MethodSet // method set of (non-interface) X
|
||||
X Value
|
||||
}
|
||||
|
||||
// The MakeClosure instruction yields a closure value whose code is
|
||||
|
|
@ -641,6 +654,7 @@ type MakeClosure struct {
|
|||
//
|
||||
// Example printed form:
|
||||
// t1 = make map[string]int t0
|
||||
// t1 = make StringIntMap t0
|
||||
//
|
||||
type MakeMap struct {
|
||||
Register
|
||||
|
|
@ -657,6 +671,7 @@ type MakeMap struct {
|
|||
//
|
||||
// Example printed form:
|
||||
// t0 = make chan int 0
|
||||
// t0 = make IntChan 0
|
||||
//
|
||||
type MakeChan struct {
|
||||
Register
|
||||
|
|
@ -677,7 +692,8 @@ type MakeChan struct {
|
|||
// created it.
|
||||
//
|
||||
// Example printed form:
|
||||
// t1 = make slice []string 1:int t0
|
||||
// t1 = make []string 1:int t0
|
||||
// t1 = make StringSlice 1:int t0
|
||||
//
|
||||
type MakeSlice struct {
|
||||
Register
|
||||
|
|
@ -905,6 +921,11 @@ type Next struct {
|
|||
// Type() reflects the actual type of the result, possibly a
|
||||
// 2-types.Tuple; AssertedType is the asserted type.
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
|
||||
// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
|
||||
// instruction arose from an explicit e.(T) operation; or token.NoPos
|
||||
// otherwise.
|
||||
//
|
||||
// Example printed form:
|
||||
// t1 = typeassert t0.(int)
|
||||
// t3 = typeassert,ok t2.(T)
|
||||
|
|
@ -1168,10 +1189,6 @@ type anInstruction struct {
|
|||
// receiver but the first true argument. Func is not used in this
|
||||
// mode.
|
||||
//
|
||||
// If the called method's receiver has non-pointer type T, but the
|
||||
// receiver supplied by the interface value has type *T, an implicit
|
||||
// load (copy) operation is performed.
|
||||
//
|
||||
// Example printed form:
|
||||
// t1 = invoke t0.String()
|
||||
// go invoke t3.Run(t2)
|
||||
|
|
@ -1244,21 +1261,25 @@ func (v *Capture) Type() types.Type { return v.typ }
|
|||
func (v *Capture) Name() string { return v.name }
|
||||
func (v *Capture) Referrers() *[]Instruction { return &v.referrers }
|
||||
func (v *Capture) Pos() token.Pos { return v.pos }
|
||||
func (v *Capture) Parent() *Function { return v.parent }
|
||||
|
||||
func (v *Global) Type() types.Type { return v.typ }
|
||||
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 *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 *Parameter) Type() types.Type { return v.typ }
|
||||
func (v *Parameter) Name() string { return v.name }
|
||||
func (v *Parameter) Referrers() *[]Instruction { return &v.referrers }
|
||||
func (v *Parameter) Pos() token.Pos { return v.pos }
|
||||
func (v *Parameter) Parent() *Function { return v.parent }
|
||||
|
||||
func (v *Alloc) Type() types.Type { return v.typ }
|
||||
func (v *Alloc) Name() string { return v.name }
|
||||
|
|
@ -1274,18 +1295,21 @@ func (v *Register) asRegister() *Register { return v }
|
|||
func (v *Register) Pos() token.Pos { return v.pos }
|
||||
func (v *Register) setPos(pos token.Pos) { v.pos = pos }
|
||||
|
||||
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.NamedType.Obj().Name() }
|
||||
func (t *Type) Pos() token.Pos { return t.NamedType.Obj().Pos() }
|
||||
func (t *Type) String() string { return t.Name() }
|
||||
func (t *Type) Type() types.Type { return t.NamedType }
|
||||
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 (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) 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 returns the package-level function of the specified name,
|
||||
// or nil if not found.
|
||||
|
|
@ -1335,7 +1359,8 @@ func (prog *Program) Value(obj types.Object) Value {
|
|||
}
|
||||
|
||||
// Package returns the SSA package corresponding to the specified
|
||||
// type-checker package object. It returns nil if not found.
|
||||
// type-checker package object.
|
||||
// It returns nil if no such SSA package has been created.
|
||||
//
|
||||
func (prog *Program) Package(pkg *types.Package) *Package {
|
||||
return prog.packages[pkg]
|
||||
|
|
|
|||
|
|
@ -45,10 +45,7 @@ func isBlankIdent(e ast.Expr) bool {
|
|||
|
||||
// isPointer returns true for types whose underlying type is a pointer.
|
||||
func isPointer(typ types.Type) bool {
|
||||
if nt, ok := typ.(*types.Named); ok {
|
||||
typ = nt.Underlying()
|
||||
}
|
||||
_, ok := typ.(*types.Pointer)
|
||||
_, ok := typ.Underlying().(*types.Pointer)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue