// typeAssert checks whether dynamic type of itf is instr.AssertedType. // It returns the extracted value on success, and panics on failure, // unless instr.CommaOk, in which case it always returns a "value,ok" tuple. // func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value { var v value err := "" if itf.t == nil { err = fmt.Sprintf("interface conversion: interface is nil, not %s", instr.AssertedType) } else if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok { v = itf err = checkInterface(i, idst, itf) } else if types.Identical(itf.t, instr.AssertedType) { v = copyVal(itf.v) // extract value } else { err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType) } if err != "" { if !instr.CommaOk { panic(err) } return tuple{zero(instr.AssertedType), false} } if instr.CommaOk { return tuple{v, true} } return v }
// Set sets the map entry for key to val, // and returns the previous entry, if any. func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) { if m.table != nil { hash := m.hasher.Hash(key) bucket := m.table[hash] var hole *entry for i, e := range bucket { if e.key == nil { hole = &bucket[i] } else if types.Identical(key, e.key) { prev = e.value bucket[i].value = value return } } if hole != nil { *hole = entry{key, value} // overwrite deleted entry } else { m.table[hash] = append(bucket, entry{key, value}) } } else { if m.hasher.memo == nil { m.hasher = MakeHasher() } hash := m.hasher.Hash(key) m.table = map[uint32][]entry{hash: {entry{key, value}}} } m.length++ return }
// computeTrackBits sets a.track to the necessary 'track' bits for the pointer queries. func (a *analysis) computeTrackBits() { var queryTypes []types.Type for v := range a.config.Queries { queryTypes = append(queryTypes, v.Type()) } for v := range a.config.IndirectQueries { queryTypes = append(queryTypes, mustDeref(v.Type())) } for _, t := range queryTypes { switch t.Underlying().(type) { case *types.Chan: a.track |= trackChan case *types.Map: a.track |= trackMap case *types.Pointer: a.track |= trackPtr case *types.Slice: a.track |= trackSlice case *types.Interface: a.track = trackAll return } if rVObj := a.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) { a.track = trackAll return } } }
// containsAllIdsOf reports whether the method identifiers of T are a // superset of those in U. If U belongs to an interface type, the // result is equal to types.Assignable(T, U), but is cheaper to compute. // // TODO(gri): make this a method of *types.MethodSet. // func containsAllIdsOf(T, U *types.MethodSet) bool { t, tlen := 0, T.Len() u, ulen := 0, U.Len() for t < tlen && u < ulen { tMeth := T.At(t).Obj() uMeth := U.At(u).Obj() tId := tMeth.Id() uId := uMeth.Id() if tId > uId { // U has a method T lacks: fail. return false } if tId < uId { // T has a method U lacks: ignore it. t++ continue } // U and T both have a method of this Id. Check types. if !types.Identical(tMeth.Type(), uMeth.Type()) { return false // type mismatch } u++ t++ } return u == ulen }
func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { if !types.Identical(x, y) { t.Errorf("%s: not equal: %s, %s", comment, x, y) } if x == y { t.Errorf("%s: identical: %v, %v", comment, x, y) } }
// At returns the map entry for the given key. // The result is nil if the entry is not present. // func (m *Map) At(key types.Type) interface{} { if m != nil && m.table != nil { for _, e := range m.table[m.hasher.Hash(key)] { if e.key != nil && types.Identical(key, e.key) { return e.value } } } return nil }
// Delete removes the entry with the given key, if any. // It returns true if the entry was found. // func (m *Map) Delete(key types.Type) bool { if m != nil && m.table != nil { hash := m.hasher.Hash(key) bucket := m.table[hash] for i, e := range bucket { if e.key != nil && types.Identical(key, e.key) { // We can't compact the bucket as it // would disturb iterators. bucket[i] = entry{} m.length-- return true } } } return false }
// isValuePreserving returns true if a conversion from ut_src to // ut_dst is value-preserving, i.e. just a change of type. // Precondition: neither argument is a named type. // func isValuePreserving(ut_src, ut_dst types.Type) bool { // Identical underlying types? if types.Identical(ut_dst, ut_src) { return true } switch ut_dst.(type) { case *types.Chan: // Conversion between channel types? _, ok := ut_src.(*types.Chan) return ok case *types.Pointer: // Conversion between pointers with identical base types? _, ok := ut_src.(*types.Pointer) return ok } return false }
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) { c := prog.ConstValue(obj) // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging if c == nil { t.Errorf("ConstValue(%s) == nil", obj) return } if !types.Identical(c.Type(), obj.Type()) { t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type()) return } if obj.Name() != "nil" { if !exact.Compare(c.Value, token.EQL, obj.Val()) { t.Errorf("ConstValue(%s).Value (%s) != %s", obj, c.Value, obj.Val()) return } } }
func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) { fn := prog.FuncValue(obj) // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging if fn == nil { if obj.Name() != "interfaceMethod" { t.Errorf("FuncValue(%s) == nil", obj) } return } if fnobj := fn.Object(); fnobj != obj { t.Errorf("FuncValue(%s).Object() == %s; value was %s", obj, fnobj, fn.Name()) return } if !types.Identical(fn.Type(), obj.Type()) { t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type()) return } }
// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil. func findVisibleErrs(prog *ssa.Program, qpos *QueryPos) map[*ssa.Global]ssa.Value { globals := make(map[*ssa.Global]ssa.Value) for _, pkg := range prog.AllPackages() { for _, mem := range pkg.Members { gbl, ok := mem.(*ssa.Global) if !ok { continue } gbltype := gbl.Type() // globals are always pointers if !types.Identical(deref(gbltype), builtinErrorType) { continue } if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) { continue } globals[gbl] = nil } } return globals }
// emitCompare emits to f code compute the boolean result of // comparison comparison 'x op y'. // func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value { xt := x.Type().Underlying() yt := y.Type().Underlying() // Special case to optimise a tagless SwitchStmt so that // these are equivalent // switch { case e: ...} // switch true { case e: ... } // if e==true { ... } // even in the case when e's type is an interface. // TODO(adonovan): opt: generalise to x==true, false!=y, etc. if x == vTrue && op == token.EQL { if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 { return y } } if types.Identical(xt, yt) { // no conversion necessary } else if _, ok := xt.(*types.Interface); ok { y = emitConv(f, y, x.Type()) } else if _, ok := yt.(*types.Interface); ok { x = emitConv(f, x, y.Type()) } else if _, ok := x.(*Const); ok { x = emitConv(f, x, y.Type()) } else if _, ok := y.(*Const); ok { y = emitConv(f, y, x.Type()) } else { // other cases, e.g. channels. No-op. } v := &BinOp{ Op: op, X: x, Y: y, } v.setPos(pos) v.setType(tBool) return f.emit(v) }
func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) { // The prefix of all assertions messages. prefix := fmt.Sprintf("VarValue(%s @ L%d)", obj, prog.Fset.Position(ref[0].Pos()).Line) v, gotAddr := prog.VarValue(obj, pkg, ref) // Kind is the concrete type of the ssa Value. gotKind := "nil" if v != nil { gotKind = fmt.Sprintf("%T", v)[len("*ssa."):] } // fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging // Check the kinds match. // "nil" indicates expected failure (e.g. optimized away). if expKind != gotKind { t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind) } // Check the types match. // If wantAddr, the expected type is the object's address. if v != nil { expType := obj.Type() if wantAddr { expType = types.NewPointer(expType) if !gotAddr { t.Errorf("%s: got value, want address", prefix) } } else if gotAddr { t.Errorf("%s: got address, want value", prefix) } if !types.Identical(v.Type(), expType) { t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType) } } }
// nil-tolerant variant of types.Identical. func sameType(x, y types.Type) bool { if x == nil { return y == nil } return y != nil && types.Identical(x, y) }
// emitConv emits to f code to convert Value val to exactly type typ, // and returns the converted value. Implicit conversions are required // by language assignability rules in assignments, parameter passing, // etc. Conversions cannot fail dynamically. // func emitConv(f *Function, val Value, typ types.Type) Value { t_src := val.Type() // Identical types? Conversion is a no-op. if types.Identical(t_src, typ) { return val } ut_dst := typ.Underlying() ut_src := t_src.Underlying() // Just a change of type, but not value or representation? if isValuePreserving(ut_src, ut_dst) { c := &ChangeType{X: val} c.setType(typ) return f.emit(c) } // Conversion to, or construction of a value of, an interface type? if _, ok := ut_dst.(*types.Interface); ok { // Assignment from one interface type to another? if _, ok := ut_src.(*types.Interface); ok { c := &ChangeInterface{X: val} c.setType(typ) return f.emit(c) } // Untyped nil constant? Return interface-typed nil constant. if ut_src == tUntypedNil { return nilConst(typ) } // Convert (non-nil) "untyped" literals to their default type. if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 { val = emitConv(f, val, DefaultType(ut_src)) } f.Pkg.Prog.needMethodsOf(val.Type()) mi := &MakeInterface{X: val} mi.setType(typ) return f.emit(mi) } // Conversion of a compile-time constant value? if c, ok := val.(*Const); ok { if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() { // Conversion of a compile-time constant to // another constant type results in a new // constant of the destination type and // (initially) the same abstract value. // We don't truncate the value yet. return NewConst(c.Value, typ) } // We're converting from constant to non-constant type, // e.g. string -> []byte/[]rune. } // A representation-changing conversion? // At least one of {ut_src,ut_dst} must be *Basic. // (The other may be []byte or []rune.) _, ok1 := ut_src.(*types.Basic) _, ok2 := ut_dst.(*types.Basic) if ok1 || ok2 { c := &Convert{X: val} c.setType(typ) return f.emit(c) } panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ)) }
// Like isTest, but checks the signature too. func isTestSig(f *Function, prefix string, sig *types.Signature) bool { return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig) }
// Ensure that, in debug mode, we can determine the ssa.Value // corresponding to every ast.Expr. func TestValueForExpr(t *testing.T) { conf := loader.Config{ParserMode: parser.ParseComments} f, err := conf.ParseFile("testdata/valueforexpr.go", nil) if err != nil { t.Error(err) return } conf.CreateFromFiles("main", f) iprog, err := conf.Load() if err != nil { t.Error(err) return } mainInfo := iprog.Created[0] prog := ssa.Create(iprog, 0) mainPkg := prog.Package(mainInfo.Pkg) mainPkg.SetDebugMode(true) mainPkg.Build() if false { // debugging for _, mem := range mainPkg.Members { if fn, ok := mem.(*ssa.Function); ok { fn.WriteTo(os.Stderr) } } } // Find the actual AST node for each canonical position. parenExprByPos := make(map[token.Pos]*ast.ParenExpr) ast.Inspect(f, func(n ast.Node) bool { if n != nil { if e, ok := n.(*ast.ParenExpr); ok { parenExprByPos[e.Pos()] = e } } return true }) // Find all annotations of form /*@kind*/. for _, c := range f.Comments { text := strings.TrimSpace(c.Text()) if text == "" || text[0] != '@' { continue } text = text[1:] pos := c.End() + 1 position := prog.Fset.Position(pos) var e ast.Expr if target := parenExprByPos[pos]; target == nil { t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text) continue } else { e = target.X } path, _ := astutil.PathEnclosingInterval(f, pos, pos) if path == nil { t.Errorf("%s: can't find AST path from root to comment: %s", position, text) continue } fn := ssa.EnclosingFunction(mainPkg, path) if fn == nil { t.Errorf("%s: can't find enclosing function", position) continue } v, gotAddr := fn.ValueForExpr(e) // (may be nil) got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.") if want := text; got != want { t.Errorf("%s: got value %q, want %q", position, got, want) } if v != nil { T := v.Type() if gotAddr { T = T.Underlying().(*types.Pointer).Elem() // deref } if !types.Identical(T, mainInfo.TypeOf(e)) { t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T) } } } }
func (x rtype) eq(_ types.Type, y interface{}) bool { return types.Identical(x.t, y.(rtype).t) }
// whicherrs takes an position to an error and tries to find all types, constants // and global value which a given error can point to and which can be checked from the // scope where the error lives. // In short, it returns a list of things that can be checked against in order to handle // an error properly. // // TODO(dmorsing): figure out if fields in errors like *os.PathError.Err // can be queried recursively somehow. func whicherrs(o *Oracle, qpos *QueryPos) (queryResult, error) { path, action := findInterestingNode(qpos.info, qpos.path) if action != actionExpr { return nil, fmt.Errorf("whicherrs wants an expression; got %s", astutil.NodeDescription(qpos.path[0])) } var expr ast.Expr var obj types.Object switch n := path[0].(type) { case *ast.ValueSpec: // ambiguous ValueSpec containing multiple names return nil, fmt.Errorf("multiple value specification") case *ast.Ident: obj = qpos.info.ObjectOf(n) expr = n case ast.Expr: expr = n default: return nil, fmt.Errorf("unexpected AST for expr: %T", n) } typ := qpos.info.TypeOf(expr) if !types.Identical(typ, builtinErrorType) { return nil, fmt.Errorf("selection is not an expression of type 'error'") } // Determine the ssa.Value for the expression. var value ssa.Value var err error if obj != nil { // def/ref of func/var object value, _, err = ssaValueForIdent(o.prog, qpos.info, obj, path) } else { value, _, err = ssaValueForExpr(o.prog, qpos.info, path) } if err != nil { return nil, err // e.g. trivially dead code } buildSSA(o) globals := findVisibleErrs(o.prog, qpos) constants := findVisibleConsts(o.prog, qpos) res := &whicherrsResult{ qpos: qpos, errpos: expr.Pos(), } // Find the instruction which initialized the // global error. If more than one instruction has stored to the global // remove the global from the set of values that we want to query. allFuncs := ssautil.AllFunctions(o.prog) for fn := range allFuncs { for _, b := range fn.Blocks { for _, instr := range b.Instrs { store, ok := instr.(*ssa.Store) if !ok { continue } gval, ok := store.Addr.(*ssa.Global) if !ok { continue } gbl, ok := globals[gval] if !ok { continue } // we already found a store to this global // The normal error define is just one store in the init // so we just remove this global from the set we want to query if gbl != nil { delete(globals, gval) } globals[gval] = store.Val } } } o.ptaConfig.AddQuery(value) for _, v := range globals { o.ptaConfig.AddQuery(v) } ptares := ptrAnalysis(o) valueptr := ptares.Queries[value] for g, v := range globals { ptr, ok := ptares.Queries[v] if !ok { continue } if !ptr.MayAlias(valueptr) { continue } res.globals = append(res.globals, g) } pts := valueptr.PointsTo() dedup := make(map[*ssa.NamedConst]bool) for _, label := range pts.Labels() { // These values are either MakeInterfaces or reflect // generated interfaces. For the purposes of this // analysis, we don't care about reflect generated ones makeiface, ok := label.Value().(*ssa.MakeInterface) if !ok { continue } constval, ok := makeiface.X.(*ssa.Const) if !ok { continue } c := constants[*constval] if c != nil && !dedup[c] { dedup[c] = true res.consts = append(res.consts, c) } } concs := pts.DynamicTypes() concs.Iterate(func(conc types.Type, _ interface{}) { // go/types is a bit annoying here. // We want to find all the types that we can // typeswitch or assert to. This means finding out // if the type pointed to can be seen by us. // // For the purposes of this analysis, the type is always // either a Named type or a pointer to one. // There are cases where error can be implemented // by unnamed types, but in that case, we can't assert to // it, so we don't care about it for this analysis. var name *types.TypeName switch t := conc.(type) { case *types.Pointer: named, ok := t.Elem().(*types.Named) if !ok { return } name = named.Obj() case *types.Named: name = t.Obj() default: return } if !isAccessibleFrom(name, qpos.info.Pkg) { return } res.types = append(res.types, &errorType{conc, name}) }) sort.Sort(membersByPosAndString(res.globals)) sort.Sort(membersByPosAndString(res.consts)) sort.Sort(sorterrorType(res.types)) return res, nil }
// Implements displays the "implements" relation as it pertains to the // selected type. If the selection is a method, 'implements' displays // the corresponding methods of the types that would have been reported // by an implements query on the receiver type. // func implements(o *Oracle, qpos *QueryPos) (queryResult, error) { // Find the selected type. path, action := findInterestingNode(qpos.info, qpos.path) var method *types.Func var T types.Type // selected type (receiver if method != nil) switch action { case actionExpr: // method? if id, ok := path[0].(*ast.Ident); ok { if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok { recv := obj.Type().(*types.Signature).Recv() if recv == nil { return nil, fmt.Errorf("this function is not a method") } method = obj T = recv.Type() } } case actionType: T = qpos.info.TypeOf(path[0].(ast.Expr)) } if T == nil { return nil, fmt.Errorf("no type or method here") } // Find all named types, even local types (which can have // methods via promotion) and the built-in "error". // // TODO(adonovan): include all packages in PTA scope too? // i.e. don't reduceScope? // var allNamed []types.Type for _, info := range o.typeInfo { for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) var msets types.MethodSetCache // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if msets.MethodSet(T).Len() == 0 { continue // empty interface } if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T interface, U interface if !types.Identical(T, U) { if types.AssignableTo(U, T) { to = append(to, U) } if types.AssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.AssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T concrete, U interface if types.AssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { fromPtr = append(fromPtr, U) } } } var pos interface{} = qpos if nt, ok := deref(T).(*types.Named); ok { pos = nt.Obj() } // Sort types (arbitrarily) to ensure test determinism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils if method != nil { for _, t := range to { toMethod = append(toMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range from { fromMethod = append(fromMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } for _, t := range fromPtr { fromPtrMethod = append(fromPtrMethod, types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) } } return &implementsResult{qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod}, nil }
// peers enumerates, for a given channel send (or receive) operation, // the set of possible receives (or sends) that correspond to it. // // TODO(adonovan): support reflect.{Select,Recv,Send,Close}. // TODO(adonovan): permit the user to query based on a MakeChan (not send/recv), // or the implicit receive in "for v := range ch". func peers(o *Oracle, qpos *QueryPos) (queryResult, error) { opPos := findOp(qpos) if opPos == token.NoPos { return nil, fmt.Errorf("there is no channel operation here") } buildSSA(o) var queryOp chanOp // the originating send or receive operation var ops []chanOp // all sends/receives of opposite direction // Look at all channel operations in the whole ssa.Program. // Build a list of those of same type as the query. allFuncs := ssautil.AllFunctions(o.prog) for fn := range allFuncs { for _, b := range fn.Blocks { for _, instr := range b.Instrs { for _, op := range chanOps(instr) { ops = append(ops, op) if op.pos == opPos { queryOp = op // we found the query op } } } } } if queryOp.ch == nil { return nil, fmt.Errorf("ssa.Instruction for send/receive not found") } // Discard operations of wrong channel element type. // Build set of channel ssa.Values as query to pointer analysis. // We compare channels by element types, not channel types, to // ignore both directionality and type names. queryType := queryOp.ch.Type() queryElemType := queryType.Underlying().(*types.Chan).Elem() o.ptaConfig.AddQuery(queryOp.ch) i := 0 for _, op := range ops { if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) { o.ptaConfig.AddQuery(op.ch) ops[i] = op i++ } } ops = ops[:i] // Run the pointer analysis. ptares := ptrAnalysis(o) // Find the points-to set. queryChanPtr := ptares.Queries[queryOp.ch] // Ascertain which make(chan) labels the query's channel can alias. var makes []token.Pos for _, label := range queryChanPtr.PointsTo().Labels() { makes = append(makes, label.Pos()) } sort.Sort(byPos(makes)) // Ascertain which channel operations can alias the same make(chan) labels. var sends, receives, closes []token.Pos for _, op := range ops { if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) { switch op.dir { case types.SendOnly: sends = append(sends, op.pos) case types.RecvOnly: receives = append(receives, op.pos) case types.SendRecv: closes = append(closes, op.pos) } } } sort.Sort(byPos(sends)) sort.Sort(byPos(receives)) sort.Sort(byPos(closes)) return &peersResult{ queryPos: opPos, queryType: queryType, makes: makes, sends: sends, receives: receives, closes: closes, }, nil }