// 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 = 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 }
// 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 } } }
// 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 }
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 }
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 } }
// 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 }
// 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) } } }
// 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) }
func (x rtype) eq(_ types.Type, y interface{}) bool { return types.Identical(x.t, y.(rtype).t) }
// 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) }
// 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 isSlice(typ types.Type, bkind types.BasicKind) bool { t, ok := typ.Underlying().(*types.Slice) return ok && types.Identical(t.Elem().Underlying(), types.Typ[bkind]) }
// 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)) }
func (fr *frame) convert(v *govalue, dsttyp types.Type) *govalue { b := fr.builder // If it's a stack allocated value, we'll want to compare the // value type, not the pointer type. srctyp := v.typ // Get the underlying type, if any. origdsttyp := dsttyp dsttyp = dsttyp.Underlying() srctyp = srctyp.Underlying() // Identical (underlying) types? Just swap in the destination type. if types.Identical(srctyp, dsttyp) { return newValue(v.value, origdsttyp) } // Both pointer types with identical underlying types? Same as above. if srctyp, ok := srctyp.(*types.Pointer); ok { if dsttyp, ok := dsttyp.(*types.Pointer); ok { srctyp := srctyp.Elem().Underlying() dsttyp := dsttyp.Elem().Underlying() if types.Identical(srctyp, dsttyp) { return newValue(v.value, origdsttyp) } } } // string -> if isString(srctyp) { // (untyped) string -> string // XXX should untyped strings be able to escape go/types? if isString(dsttyp) { return newValue(v.value, origdsttyp) } // string -> []byte if isSlice(dsttyp, types.Byte) { value := v.value strdata := fr.builder.CreateExtractValue(value, 0, "") strlen := fr.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := fr.createMalloc(strlen) fr.memcpy(newdata, strdata, strlen) struct_ := llvm.Undef(fr.types.ToLLVM(dsttyp)) struct_ = fr.builder.CreateInsertValue(struct_, newdata, 0, "") struct_ = fr.builder.CreateInsertValue(struct_, strlen, 1, "") struct_ = fr.builder.CreateInsertValue(struct_, strlen, 2, "") return newValue(struct_, origdsttyp) } // string -> []rune if isSlice(dsttyp, types.Rune) { return fr.stringToRuneSlice(v) } } // []byte -> string if isSlice(srctyp, types.Byte) && isString(dsttyp) { value := v.value data := fr.builder.CreateExtractValue(value, 0, "") len := fr.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := fr.createMalloc(len) fr.memcpy(newdata, data, len) struct_ := llvm.Undef(fr.types.ToLLVM(types.Typ[types.String])) struct_ = fr.builder.CreateInsertValue(struct_, newdata, 0, "") struct_ = fr.builder.CreateInsertValue(struct_, len, 1, "") return newValue(struct_, types.Typ[types.String]) } // []rune -> string if isSlice(srctyp, types.Rune) && isString(dsttyp) { return fr.runeSliceToString(v) } // rune -> string if isString(dsttyp) && isInteger(srctyp) { return fr.runeToString(v) } // Unsafe pointer conversions. llvm_type := fr.types.ToLLVM(dsttyp) if dsttyp == types.Typ[types.UnsafePointer] { // X -> unsafe.Pointer if _, isptr := srctyp.(*types.Pointer); isptr { return newValue(v.value, origdsttyp) } else if srctyp == types.Typ[types.Uintptr] { value := b.CreateIntToPtr(v.value, llvm_type, "") return newValue(value, origdsttyp) } } else if srctyp == types.Typ[types.UnsafePointer] { // unsafe.Pointer -> X if _, isptr := dsttyp.(*types.Pointer); isptr { return newValue(v.value, origdsttyp) } else if dsttyp == types.Typ[types.Uintptr] { value := b.CreatePtrToInt(v.value, llvm_type, "") return newValue(value, origdsttyp) } } lv := v.value srcType := lv.Type() switch srcType.TypeKind() { case llvm.IntegerTypeKind: switch llvm_type.TypeKind() { case llvm.IntegerTypeKind: srcBits := srcType.IntTypeWidth() dstBits := llvm_type.IntTypeWidth() delta := srcBits - dstBits switch { case delta < 0: if !isUnsigned(srctyp) { lv = b.CreateSExt(lv, llvm_type, "") } else { lv = b.CreateZExt(lv, llvm_type, "") } case delta > 0: lv = b.CreateTrunc(lv, llvm_type, "") } return newValue(lv, origdsttyp) case llvm.FloatTypeKind, llvm.DoubleTypeKind: if !isUnsigned(v.Type()) { lv = b.CreateSIToFP(lv, llvm_type, "") } else { lv = b.CreateUIToFP(lv, llvm_type, "") } return newValue(lv, origdsttyp) } case llvm.DoubleTypeKind: switch llvm_type.TypeKind() { case llvm.FloatTypeKind: lv = b.CreateFPTrunc(lv, llvm_type, "") return newValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return newValue(lv, origdsttyp) } case llvm.FloatTypeKind: switch llvm_type.TypeKind() { case llvm.DoubleTypeKind: lv = b.CreateFPExt(lv, llvm_type, "") return newValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return newValue(lv, origdsttyp) } } // Complex -> complex. Complexes are only convertible to other // complexes, contant conversions aside. So we can just check the // source type here; given that the types are not identical // (checked above), we can assume the destination type is the alternate // complex type. if isComplex(srctyp) { var fpcast func(llvm.Builder, llvm.Value, llvm.Type, string) llvm.Value var fptype llvm.Type if srctyp == types.Typ[types.Complex64] { fpcast = (llvm.Builder).CreateFPExt fptype = llvm.DoubleType() } else { fpcast = (llvm.Builder).CreateFPTrunc fptype = llvm.FloatType() } if fpcast != nil { realv := b.CreateExtractValue(lv, 0, "") imagv := b.CreateExtractValue(lv, 1, "") realv = fpcast(b, realv, fptype, "") imagv = fpcast(b, imagv, fptype, "") lv = llvm.Undef(fr.types.ToLLVM(dsttyp)) lv = b.CreateInsertValue(lv, realv, 0, "") lv = b.CreateInsertValue(lv, imagv, 1, "") return newValue(lv, origdsttyp) } } panic(fmt.Sprintf("unimplemented conversion: %s (%s) -> %s", v.typ, lv.Type(), origdsttyp)) }