// 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.IsIdentical(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 case *types.Signature: // Conversion between f(T) function and (T) func f() method? // TODO(adonovan): is this sound? Discuss with gri. _, ok := ut_src.(*types.Signature) return ok } return false }
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 := 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) addr=%t\n", prefix, v, gotKind, expKind, wantAddr) // 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 !types.IsIdentical(v.Type(), expType) { t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType) } } }
// Set sets the map entry for key to val, // and returns the previous entry, if any. func (m *M) 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.IsIdentical(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 }
// 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.IsIdentical(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 }
func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) { tIface, _ := c.typ.Underlying().(*types.Interface) for ifaceObj := range delta { tDyn, v, indirect := a.taggedValue(ifaceObj) if tDyn == nil { panic("not a tagged value") } if indirect { // TODO(adonovan): we'll need to implement this // when we start creating indirect tagged objects. panic("indirect tagged object") } if tIface != nil { if types.IsAssignableTo(tDyn, tIface) { if a.addLabel(c.dst, ifaceObj) { a.addWork(c.dst) } } } else { if types.IsIdentical(tDyn, c.typ) { // Copy entire payload to dst. // // TODO(adonovan): opt: if tConc is // nonpointerlike we can skip this // entire constraint, perhaps. We // only care about pointers among the // fields. a.onlineCopyN(c.dst, v, a.sizeof(tDyn)) } } } }
// 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. // func emitConv(f *Function, val Value, typ types.Type) Value { t_src := val.Type() // Identical types? Conversion is a no-op. if types.IsIdentical(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 { return emitTypeAssert(f, val, typ) } // Untyped nil literal? Return interface-typed nil literal. if ut_src == tUntypedNil { return nilLiteral(typ) } // Convert (non-nil) "untyped" literals to their default type. // TODO(gri): expose types.isUntyped(). if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 { val = emitConv(f, val, DefaultType(ut_src)) } mi := &MakeInterface{ X: val, Methods: f.Prog.MethodSet(t_src), } mi.setType(typ) return f.emit(mi) } // Conversion of a literal to a non-interface type results in // a new literal of the destination type and (initially) the // same abstract value. We don't compute the representation // 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) } // A representation-changing conversion. c := &Convert{X: val} c.setType(typ) return f.emit(c) }
func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { if !types.IsIdentical(x, y) { t.Errorf("%s: not equal: %s, %s", comment, x, y) } if x == y { t.Errorf("%s: identical: %p, %p", comment, x, y) } }
// At returns the map entry for the given key. // The result is nil if the entry is not present. // func (m *M) 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.IsIdentical(key, e.key) { return e.value } } } return nil }
// testMainSlice emits to fn code to construct a slice of type slice // (one of []testing.Internal{Test,Benchmark,Example}) for all // functions in this package whose name starts with prefix (one of // "Test", "Benchmark" or "Example") and whose type is appropriate. // It returns the slice value. // func testMainSlice(fn *Function, prefix string, slice types.Type) Value { tElem := slice.(*types.Slice).Elem() tFunc := tElem.Underlying().(*types.Struct).Field(1).Type() var testfuncs []*Function for name, mem := range fn.Pkg.Members { if fn, ok := mem.(*Function); ok && isTest(name, prefix) && types.IsIdentical(fn.Signature, tFunc) { testfuncs = append(testfuncs, fn) } } if testfuncs == nil { return nilConst(slice) } tString := types.Typ[types.String] tPtrString := types.NewPointer(tString) tPtrElem := types.NewPointer(tElem) tPtrFunc := types.NewPointer(tFunc) // Emit: array = new [n]testing.InternalTest tArray := types.NewArray(tElem, int64(len(testfuncs))) array := emitNew(fn, tArray, token.NoPos) array.Comment = "test main" for i, testfunc := range testfuncs { // Emit: pitem = &array[i] ia := &IndexAddr{X: array, Index: intConst(int64(i))} ia.setType(tPtrElem) pitem := fn.emit(ia) // Emit: pname = &pitem.Name fa := &FieldAddr{X: pitem, Field: 0} // .Name fa.setType(tPtrString) pname := fn.emit(fa) // Emit: *pname = "testfunc" emitStore(fn, pname, NewConst(exact.MakeString(testfunc.Name()), tString)) // Emit: pfunc = &pitem.F fa = &FieldAddr{X: pitem, Field: 1} // .F fa.setType(tPtrFunc) pfunc := fn.emit(fa) // Emit: *pfunc = testfunc emitStore(fn, pfunc, testfunc) } // Emit: slice array[:] sl := &Slice{X: array} sl.setType(slice) return fn.emit(sl) }
// Delete removes the entry with the given key, if any. // It returns true if the entry was found. // func (m *M) 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.IsIdentical(key, e.key) { // We can't compact the bucket as it // would disturb iterators. bucket[i] = entry{} m.length-- return true } } } return false }
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 { 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.IsIdentical(fn.Type(), obj.Type()) { t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type()) return } }
func (c *rVSetBytesConstraint) solve(a *analysis, _ *node, delta nodeset) { for vObj := range delta { tDyn, slice, indirect := a.taggedValue(vObj) if indirect { // TODO(adonovan): we'll need to implement this // when we start creating indirect tagged objects. panic("indirect tagged object") } tSlice, ok := tDyn.Underlying().(*types.Slice) if ok && types.IsIdentical(tSlice.Elem(), types.Typ[types.Uint8]) { if a.onlineCopy(slice, c.x) { a.addWork(slice) } } } }
// 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 { // Simplify infallible assertions. txi := x.Type().Underlying().(*types.Interface) if ti, ok := t.Underlying().(*types.Interface); ok { if types.IsIdentical(ti, txi) { return x } if isSuperinterface(ti, txi) { c := &ChangeInterface{X: x} c.setType(t) return f.emit(c) } } a := &TypeAssert{X: x, AssertedType: t} a.setType(t) return f.emit(a) }
// isErrorMethodCall reports whether the call is of a method with signature // func Error() string // where "string" is the universe's string type. We know the method is called "Error". func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { typ := f.pkg.types[call] if typ != nil { // We know it's called "Error", so just check the function signature. return types.IsIdentical(f.pkg.types[call.Fun], stringerMethodType) } // Without types, we can still check by hand. // Is it a selector expression? Otherwise it's a function call, not a method call. sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return false } // The package is type-checked, so if there are no arguments, we're done. if len(call.Args) > 0 { return false } // Check the type of the method declaration typ = f.pkg.types[sel] if typ == nil { return false } // The type must be a signature, but be sure for safety. sig, ok := typ.(*types.Signature) if !ok { return false } // There must be a receiver for it to be a method call. Otherwise it is // a function, not something that satisfies the error interface. if sig.Recv() == nil { return false } // There must be no arguments. Already verified by type checking, but be thorough. if sig.Params().Len() > 0 { return false } // Finally the real questions. // There must be one result. if sig.Results().Len() != 1 { return false } // It must have return type "string" from the universe. return sig.Results().At(0).Type() == types.Typ[types.String] }
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.IsIdentical(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 } } }
// isSuperinterface returns true if x is a superinterface of y, // i.e. x's methods are a subset of y's. // func isSuperinterface(x, y *types.Interface) bool { if y.NumMethods() < x.NumMethods() { return false } // TODO(adonovan): opt: this is quadratic. outer: for i, n := 0, x.NumMethods(); i < n; i++ { xm := x.Method(i) for j, m := 0, y.NumMethods(); j < m; j++ { ym := y.Method(j) if MakeId(xm.Name(), xm.Pkg()) == MakeId(ym.Name(), ym.Pkg()) { if !types.IsIdentical(xm.Type(), ym.Type()) { return false // common name but conflicting types } continue outer } } return false // y doesn't have this method } return true }
// 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.IsIdentical(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) }
// checkShadowing checks whether the identifier shadows an identifier in an outer scope. func (f *File) checkShadowing(ident *ast.Ident) { obj := f.pkg.idents[ident] if obj == nil { return } // obj.Parent.Parent is the surrounding scope. If we can find another declaration // starting from there, we have a shadowed variable. shadowed := obj.Parent().Parent().LookupParent(obj.Name()) if shadowed == nil { return } // Don't complain if it's shadowing a universe-declared variable; that's fine. if shadowed.Parent() == types.Universe { return } if *strictShadowing { // The shadowed variable must appear before this one to be an instance of shadowing. if shadowed.Pos() > ident.Pos() { return } } else { // Don't complain if the span of validity of the shadowed variable doesn't include // the shadowing variable. span, ok := f.pkg.spans[shadowed] if !ok { f.Badf(ident.Pos(), "internal error: no range for %s", ident.Name) return } if !span.contains(ident.Pos()) { return } } // Don't complain if the types differ: that implies the programmer really wants two variables. if types.IsIdentical(obj.Type(), shadowed.Type()) { f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos())) } }
// 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.IsIdentical(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 case *types.Signature: // Conversion from (T) func f() method to f(T) function? _, ok := ut_src.(*types.Signature) return ok } return false }
func (v *LLVMValue) Convert(dsttyp types.Type) Value { b := v.compiler.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.IsIdentical(srctyp, dsttyp) { // A method converted to a function type without the // receiver is where we convert a "method value" into a // function. if srctyp, ok := srctyp.(*types.Signature); ok && srctyp.Recv() != nil { if dsttyp, ok := dsttyp.(*types.Signature); ok && dsttyp.Recv() == nil { return v.convertMethodValue(origdsttyp) } } // TODO avoid load here by reusing pointer value, if exists. return v.compiler.NewValue(v.LLVMValue(), 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.IsIdentical(srctyp, dsttyp) { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } } // Convert from an interface type. if _, isinterface := srctyp.(*types.Interface); isinterface { if interface_, isinterface := dsttyp.(*types.Interface); isinterface { return v.mustConvertI2I(interface_) } else { return v.mustConvertI2V(origdsttyp) } } // Converting to an interface type. if interface_, isinterface := dsttyp.(*types.Interface); isinterface { return v.convertV2I(interface_) } byteslice := types.NewSlice(types.Typ[types.Byte]) runeslice := types.NewSlice(types.Typ[types.Rune]) // string -> if isString(srctyp) { // (untyped) string -> string // XXX should untyped strings be able to escape go/types? if isString(dsttyp) { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } // string -> []byte if types.IsIdentical(dsttyp, byteslice) { c := v.compiler value := v.LLVMValue() strdata := c.builder.CreateExtractValue(value, 0, "") strlen := c.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := c.builder.CreateArrayMalloc(strdata.Type().ElementType(), strlen, "") memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)") c.builder.CreateCall(memcpy, []llvm.Value{ c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""), c.builder.CreatePtrToInt(strdata, c.target.IntPtrType(), ""), strlen, }, "") strdata = newdata struct_ := llvm.Undef(c.types.ToLLVM(byteslice)) struct_ = c.builder.CreateInsertValue(struct_, strdata, 0, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 1, "") struct_ = c.builder.CreateInsertValue(struct_, strlen, 2, "") return c.NewValue(struct_, byteslice) } // string -> []rune if types.IsIdentical(dsttyp, runeslice) { return v.stringToRuneSlice() } } // []byte -> string if types.IsIdentical(srctyp, byteslice) && isString(dsttyp) { c := v.compiler value := v.LLVMValue() data := c.builder.CreateExtractValue(value, 0, "") len := c.builder.CreateExtractValue(value, 1, "") // Data must be copied, to prevent changes in // the byte slice from mutating the string. newdata := c.builder.CreateArrayMalloc(data.Type().ElementType(), len, "") memcpy := c.NamedFunction("runtime.memcpy", "func(uintptr, uintptr, uintptr)") c.builder.CreateCall(memcpy, []llvm.Value{ c.builder.CreatePtrToInt(newdata, c.target.IntPtrType(), ""), c.builder.CreatePtrToInt(data, c.target.IntPtrType(), ""), len, }, "") data = newdata struct_ := llvm.Undef(c.types.ToLLVM(types.Typ[types.String])) struct_ = c.builder.CreateInsertValue(struct_, data, 0, "") struct_ = c.builder.CreateInsertValue(struct_, len, 1, "") return c.NewValue(struct_, types.Typ[types.String]) } // []rune -> string if types.IsIdentical(srctyp, runeslice) && isString(dsttyp) { return v.runeSliceToString() } // rune -> string if isString(dsttyp) && isInteger(srctyp) { return v.runeToString() } // TODO other special conversions? llvm_type := v.compiler.types.ToLLVM(dsttyp) // Unsafe pointer conversions. if dsttyp == types.Typ[types.UnsafePointer] { // X -> unsafe.Pointer if _, isptr := srctyp.(*types.Pointer); isptr { value := b.CreatePtrToInt(v.LLVMValue(), llvm_type, "") return v.compiler.NewValue(value, origdsttyp) } else if srctyp == types.Typ[types.Uintptr] { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } else if srctyp == types.Typ[types.UnsafePointer] { // unsafe.Pointer -> X if _, isptr := dsttyp.(*types.Pointer); isptr { value := b.CreateIntToPtr(v.LLVMValue(), llvm_type, "") return v.compiler.NewValue(value, origdsttyp) } else if dsttyp == types.Typ[types.Uintptr] { return v.compiler.NewValue(v.LLVMValue(), origdsttyp) } } lv := v.LLVMValue() 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: // TODO check if (un)signed, use S/ZExt accordingly. lv = b.CreateZExt(lv, llvm_type, "") case delta > 0: lv = b.CreateTrunc(lv, llvm_type, "") } return v.compiler.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 v.compiler.NewValue(lv, origdsttyp) } case llvm.DoubleTypeKind: switch llvm_type.TypeKind() { case llvm.FloatTypeKind: lv = b.CreateFPTrunc(lv, llvm_type, "") return v.compiler.NewValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.NewValue(lv, origdsttyp) } case llvm.FloatTypeKind: switch llvm_type.TypeKind() { case llvm.DoubleTypeKind: lv = b.CreateFPExt(lv, llvm_type, "") return v.compiler.NewValue(lv, origdsttyp) case llvm.IntegerTypeKind: if !isUnsigned(dsttyp) { lv = b.CreateFPToSI(lv, llvm_type, "") } else { lv = b.CreateFPToUI(lv, llvm_type, "") } return v.compiler.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(*Builder, llvm.Value, llvm.Type, string) llvm.Value var fptype llvm.Type if srctyp == types.Typ[types.Complex64] { fpcast = (*Builder).CreateFPExt fptype = llvm.DoubleType() } else { fpcast = (*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(v.compiler.types.ToLLVM(dsttyp)) lv = b.CreateInsertValue(lv, realv, 0, "") lv = b.CreateInsertValue(lv, imagv, 1, "") return v.compiler.NewValue(lv, origdsttyp) } } srcstr := v.compiler.types.TypeString(v.typ) dststr := v.compiler.types.TypeString(origdsttyp) panic(fmt.Sprintf("unimplemented conversion: %s -> %s", srcstr, dststr)) }
// nil-tolerant variant of types.IsIdentical. func sameType(x, y types.Type) bool { if x == nil { return y == nil } return y != nil && types.IsIdentical(x, y) }
func (x rtype) eq(_ types.Type, y interface{}) bool { return types.IsIdentical(x.t, y.(rtype).t) }
// Implements displays the "implements" relation as it pertains to the // selected type. // func implements(o *Oracle, qpos *QueryPos) (queryResult, error) { // Find the selected type. // TODO(adonovan): fix: make it work on qualified Idents too. path, action := findInterestingNode(qpos.info, qpos.path) if action != actionType { return nil, fmt.Errorf("no type here") } T := qpos.info.TypeOf(path[0].(ast.Expr)) if T == nil { return nil, fmt.Errorf("no type 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 id, obj := range info.Objects { if obj, ok := obj.(*types.TypeName); ok && obj.Pos() == id.Pos() { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if T.MethodSet().Len() == 0 { continue // empty interface } if isInterface(U) { if U.MethodSet().Len() == 0 { continue // empty interface } // T interface, U interface if !types.IsIdentical(T, U) { if types.IsAssignableTo(U, T) { to = append(to, U) } if types.IsAssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.IsAssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.IsAssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if U.MethodSet().Len() == 0 { continue // empty interface } // T concrete, U interface if types.IsAssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.IsAssignableTo(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 nondeterminism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) return &implementsResult{T, pos, to, from, fromPtr}, nil }
func returnsError(f *ssa.Function) bool { results := f.Signature.Results() n := results.Len() return n > 0 && types.IsIdentical(results.At(n-1).Type(), errorType) }
func describeValue(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeValueResult, error) { 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: // Is this reachable? return nil, fmt.Errorf("unexpected AST for expr: %T", n) } typ := qpos.info.TypeOf(expr) constVal := qpos.info.ValueOf(expr) // From this point on, we cannot fail with an error. // Failure to run the pointer analysis will be reported later. // // Our disposition to pointer analysis may be one of the following: // - ok: ssa.Value was const or func. // - error: no ssa.Value for expr (e.g. trivially dead code) // - ok: ssa.Value is non-pointerlike // - error: no Pointer for ssa.Value (e.g. analytically unreachable) // - ok: Pointer has empty points-to set // - ok: Pointer has non-empty points-to set // ptaErr is non-nil only in the "error:" cases. var ptaErr error var ptrs []pointerResult // Only run pointer analysis on pointerlike expression types. if pointer.CanPoint(typ) { // Determine the ssa.Value for the expression. var value ssa.Value if obj != nil { // def/ref of func/var/const object value, ptaErr = ssaValueForIdent(o.prog, qpos.info, obj, path) } else { // any other expression if qpos.info.ValueOf(path[0].(ast.Expr)) == nil { // non-constant? value, ptaErr = ssaValueForExpr(o.prog, qpos.info, path) } } if value != nil { // TODO(adonovan): IsIdentical may be too strict; // perhaps we need is-assignable or even // has-same-underlying-representation? indirect := types.IsIdentical(types.NewPointer(typ), value.Type()) ptrs, ptaErr = describePointer(o, value, indirect) } } return &describeValueResult{ qpos: qpos, expr: expr, typ: typ, constVal: constVal, obj: obj, ptaErr: ptaErr, ptrs: ptrs, }, nil }
func (c *PkgContext) translateExpr(expr ast.Expr) string { exprType := c.info.Types[expr] if value, valueFound := c.info.Values[expr]; valueFound { basic := types.Typ[types.String] if value.Kind() != exact.String { // workaround for bug in go/types basic = exprType.Underlying().(*types.Basic) } switch { case basic.Info()&types.IsBoolean != 0: return strconv.FormatBool(exact.BoolVal(value)) case basic.Info()&types.IsInteger != 0: if is64Bit(basic) { d, _ := exact.Uint64Val(value) return fmt.Sprintf("new %s(%d, %d)", c.typeName(exprType), d>>32, d&(1<<32-1)) } d, _ := exact.Int64Val(value) return strconv.FormatInt(d, 10) case basic.Info()&types.IsFloat != 0: f, _ := exact.Float64Val(value) return strconv.FormatFloat(f, 'g', -1, 64) case basic.Info()&types.IsComplex != 0: r, _ := exact.Float64Val(exact.Real(value)) i, _ := exact.Float64Val(exact.Imag(value)) if basic.Kind() == types.UntypedComplex { exprType = types.Typ[types.Complex128] } return fmt.Sprintf("new %s(%s, %s)", c.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64)) case basic.Info()&types.IsString != 0: buffer := bytes.NewBuffer(nil) for _, r := range []byte(exact.StringVal(value)) { switch r { case '\b': buffer.WriteString(`\b`) case '\f': buffer.WriteString(`\f`) case '\n': buffer.WriteString(`\n`) case '\r': buffer.WriteString(`\r`) case '\t': buffer.WriteString(`\t`) case '\v': buffer.WriteString(`\v`) case '"': buffer.WriteString(`\"`) case '\\': buffer.WriteString(`\\`) default: if r < 0x20 || r > 0x7E { fmt.Fprintf(buffer, `\x%02X`, r) continue } buffer.WriteByte(r) } } return `"` + buffer.String() + `"` default: panic("Unhandled constant type: " + basic.String()) } } switch e := expr.(type) { case *ast.CompositeLit: if ptrType, isPointer := exprType.(*types.Pointer); isPointer { exprType = ptrType.Elem() } collectIndexedElements := func(elementType types.Type) []string { elements := make([]string, 0) i := 0 zero := c.zeroValue(elementType) for _, element := range e.Elts { if kve, isKve := element.(*ast.KeyValueExpr); isKve { key, _ := exact.Int64Val(c.info.Values[kve.Key]) i = int(key) element = kve.Value } for len(elements) <= i { elements = append(elements, zero) } elements[i] = c.translateExprToType(element, elementType) i++ } return elements } switch t := exprType.Underlying().(type) { case *types.Array: elements := collectIndexedElements(t.Elem()) if len(elements) != 0 { zero := c.zeroValue(t.Elem()) for len(elements) < int(t.Len()) { elements = append(elements, zero) } return createListComposite(t.Elem(), elements) } return fmt.Sprintf("Go$makeArray(%s, %d, function() { return %s; })", toArrayType(t.Elem()), t.Len(), c.zeroValue(t.Elem())) case *types.Slice: return fmt.Sprintf("new %s(%s)", c.typeName(exprType), createListComposite(t.Elem(), collectIndexedElements(t.Elem()))) case *types.Map: elements := make([]string, len(e.Elts)*2) for i, element := range e.Elts { kve := element.(*ast.KeyValueExpr) elements[i*2] = c.translateExprToType(kve.Key, t.Key()) elements[i*2+1] = c.translateExprToType(kve.Value, t.Elem()) } return fmt.Sprintf("new %s([%s])", c.typeName(exprType), strings.Join(elements, ", ")) case *types.Struct: elements := make([]string, t.NumFields()) isKeyValue := true if len(e.Elts) != 0 { _, isKeyValue = e.Elts[0].(*ast.KeyValueExpr) } if !isKeyValue { for i, element := range e.Elts { elements[i] = c.translateExprToType(element, t.Field(i).Type()) } } if isKeyValue { for i := range elements { elements[i] = c.zeroValue(t.Field(i).Type()) } for _, element := range e.Elts { kve := element.(*ast.KeyValueExpr) for j := range elements { if kve.Key.(*ast.Ident).Name == t.Field(j).Name() { elements[j] = c.translateExprToType(kve.Value, t.Field(j).Type()) break } } } } if named, isNamed := exprType.(*types.Named); isNamed { return fmt.Sprintf("new %s(%s)", c.objectName(named.Obj()), strings.Join(elements, ", ")) } structVar := c.newVariable("_struct") c.translateTypeSpec(&ast.TypeSpec{ Name: c.newIdent(structVar, t), Type: e.Type, }) return fmt.Sprintf("new %s(%s)", structVar, strings.Join(elements, ", ")) default: panic(fmt.Sprintf("Unhandled CompositeLit type: %T\n", t)) } case *ast.FuncLit: return strings.TrimSpace(string(c.CatchOutput(func() { c.newScope(func() { params := c.translateParams(e.Type) closurePrefix := "(" closureSuffix := ")" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } c.Printf("%sfunction(%s) {", closurePrefix, strings.Join(params, ", ")) c.Indent(func() { c.translateFunctionBody(e.Body.List, exprType.(*types.Signature)) }) c.Printf("}%s", closureSuffix) }) }))) case *ast.UnaryExpr: switch e.Op { case token.AND: switch c.info.Types[e.X].Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) default: if _, isComposite := e.X.(*ast.CompositeLit); isComposite { return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.translateExpr(e.X), c.typeName(c.info.Types[e])) } closurePrefix := "" closureSuffix := "" if len(c.escapingVars) != 0 { list := strings.Join(c.escapingVars, ", ") closurePrefix = "(function(" + list + ") { return " closureSuffix = "; })(" + list + ")" } vVar := c.newVariable("v") return fmt.Sprintf("%snew %s(function() { return %s; }, function(%s) { %s; })%s", closurePrefix, c.typeName(exprType), c.translateExpr(e.X), vVar, c.translateAssign(e.X, vVar), closureSuffix) } case token.ARROW: return "undefined" } t := c.info.Types[e.X] basic := t.Underlying().(*types.Basic) op := e.Op.String() switch e.Op { case token.ADD: return c.translateExpr(e.X) case token.SUB: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.high, -%s.low))", x, c.translateExpr(e.X), c.typeName(t), x, x) } if basic.Info()&types.IsComplex != 0 { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(-%s.real, -%s.imag))", x, c.translateExpr(e.X), c.typeName(t), x, x) } case token.XOR: if is64Bit(basic) { x := c.newVariable("x") return fmt.Sprintf("(%s = %s, new %s(~%s.high, ~%s.low >>> 0))", x, c.translateExpr(e.X), c.typeName(t), x, x) } op = "~" } return fixNumber(fmt.Sprintf("%s%s", op, c.translateExpr(e.X)), basic) case *ast.BinaryExpr: if e.Op == token.NEQ { return fmt.Sprintf("!(%s)", c.translateExpr(&ast.BinaryExpr{ X: e.X, Op: token.EQL, Y: e.Y, })) } t := c.info.Types[e.X] t2 := c.info.Types[e.Y] _, isInterface := t2.Underlying().(*types.Interface) if isInterface { t = t2 } if basic, isBasic := t.Underlying().(*types.Basic); isBasic && basic.Info()&types.IsNumeric != 0 { if is64Bit(basic) { var expr string switch e.Op { case token.MUL: return fmt.Sprintf("Go$mul64(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.QUO: return fmt.Sprintf("Go$div64(%s, %s, false)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.REM: return fmt.Sprintf("Go$div64(%s, %s, true)", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL: return fmt.Sprintf("Go$shiftLeft64(%s, %s)", c.translateExpr(e.X), c.flatten64(e.Y)) case token.SHR: return fmt.Sprintf("Go$shiftRight%s(%s, %s)", toJavaScriptType(basic), c.translateExpr(e.X), c.flatten64(e.Y)) case token.EQL: expr = "x.high === y.high && x.low === y.low" case token.LSS: expr = "x.high < y.high || (x.high === y.high && x.low < y.low)" case token.LEQ: expr = "x.high < y.high || (x.high === y.high && x.low <= y.low)" case token.GTR: expr = "x.high > y.high || (x.high === y.high && x.low > y.low)" case token.GEQ: expr = "x.high > y.high || (x.high === y.high && x.low >= y.low)" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.high %s y.high, x.low %s y.low)", c.typeName(t), e.Op, e.Op) case token.AND, token.OR, token.XOR: expr = fmt.Sprintf("new %s(x.high %s y.high, (x.low %s y.low) >>> 0)", c.typeName(t), e.Op, e.Op) case token.AND_NOT: expr = fmt.Sprintf("new %s(x.high &~ y.high, (x.low &~ y.low) >>> 0)", c.typeName(t)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } if basic.Info()&types.IsComplex != 0 { var expr string switch e.Op { case token.EQL: expr = "x.real === y.real && x.imag === y.imag" case token.ADD, token.SUB: expr = fmt.Sprintf("new %s(x.real %s y.real, x.imag %s y.imag)", c.typeName(t), e.Op, e.Op) case token.MUL: expr = fmt.Sprintf("new %s(x.real * y.real - x.imag * y.imag, x.real * y.imag + x.imag * y.real)", c.typeName(t)) case token.QUO: return fmt.Sprintf("Go$divComplex(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) default: panic(e.Op) } x := c.newVariable("x") y := c.newVariable("y") expr = strings.Replace(expr, "x.", x+".", -1) expr = strings.Replace(expr, "y.", y+".", -1) return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), expr) } switch e.Op { case token.EQL: return fmt.Sprintf("%s === %s", c.translateExpr(e.X), c.translateExpr(e.Y)) case token.LSS, token.LEQ, token.GTR, token.GEQ: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.ADD, token.SUB: return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.MUL: if basic.Kind() == types.Int32 { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >> 0) + (%s << 16 >>> 16) * %s) >> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } if basic.Kind() == types.Uint32 || basic.Kind() == types.Uintptr { x := c.newVariable("x") y := c.newVariable("y") return fmt.Sprintf("(%s = %s, %s = %s, (((%s >>> 16 << 16) * %s >>> 0) + (%s << 16 >>> 16) * %s) >>> 0)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), x, y, x, y) } return fixNumber(fmt.Sprintf("%s * %s", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) case token.QUO: value := fmt.Sprintf("%s / %s", c.translateExpr(e.X), c.translateExpr(e.Y)) if basic.Info()&types.IsInteger != 0 { value = "(Go$obj = " + value + `, (Go$obj === Go$obj && Go$obj !== 1/0 && Go$obj !== -1/0) ? Go$obj : Go$throwRuntimeError("integer divide by zero"))` } switch basic.Kind() { case types.Int, types.Uint: return "(" + value + " >> 0)" // cut off decimals default: return fixNumber(value, basic) } case token.REM: return fmt.Sprintf(`(Go$obj = %s %% %s, Go$obj === Go$obj ? Go$obj : Go$throwRuntimeError("integer divide by zero"))`, c.translateExpr(e.X), c.translateExpr(e.Y)) case token.SHL, token.SHR: op := e.Op.String() if e.Op == token.SHR && basic.Info()&types.IsUnsigned != 0 { op = ">>>" } if c.info.Values[e.Y] != nil { return fixNumber(fmt.Sprintf("%s %s %s", c.translateExpr(e.X), op, c.translateExpr(e.Y)), basic) } if e.Op == token.SHR && basic.Info()&types.IsUnsigned == 0 { return fixNumber(fmt.Sprintf("(%s >> Go$min(%s, 31))", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) } y := c.newVariable("y") return fixNumber(fmt.Sprintf("(%s = %s, %s < 32 ? (%s %s %s) : 0)", y, c.translateExprToType(e.Y, types.Typ[types.Uint]), y, c.translateExpr(e.X), op, y), basic) case token.AND, token.OR, token.XOR: return fixNumber(fmt.Sprintf("(%s %s %s)", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)), basic) case token.AND_NOT: return fixNumber(fmt.Sprintf("(%s &~ %s)", c.translateExpr(e.X), c.translateExpr(e.Y)), basic) default: panic(e.Op) } } switch e.Op { case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ, token.LAND, token.LOR: return fmt.Sprintf("%s %s %s", c.translateExpr(e.X), e.Op, c.translateExpr(e.Y)) case token.EQL: switch u := t.Underlying().(type) { case *types.Struct: x := c.newVariable("x") y := c.newVariable("y") var conds []string for i := 0; i < u.NumFields(); i++ { field := u.Field(i) if field.Name() == "_" { continue } conds = append(conds, c.translateExpr(&ast.BinaryExpr{ X: c.newIdent(x+"."+field.Name(), field.Type()), Op: token.EQL, Y: c.newIdent(y+"."+field.Name(), field.Type()), })) } if len(conds) == 0 { conds = []string{"true"} } return fmt.Sprintf("(%s = %s, %s = %s, %s)", x, c.translateExpr(e.X), y, c.translateExpr(e.Y), strings.Join(conds, " && ")) case *types.Interface: return fmt.Sprintf("Go$interfaceIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExpr(e.X), c.translateExpr(e.Y)) case *types.Pointer: xUnary, xIsUnary := e.X.(*ast.UnaryExpr) yUnary, yIsUnary := e.Y.(*ast.UnaryExpr) if xIsUnary && xUnary.Op == token.AND && yIsUnary && yUnary.Op == token.AND { xIndex, xIsIndex := xUnary.X.(*ast.IndexExpr) yIndex, yIsIndex := yUnary.X.(*ast.IndexExpr) if xIsIndex && yIsIndex { return fmt.Sprintf("Go$sliceIsEqual(%s, %s, %s, %s)", c.translateExpr(xIndex.X), c.flatten64(xIndex.Index), c.translateExpr(yIndex.X), c.flatten64(yIndex.Index)) } } switch u.Elem().Underlying().(type) { case *types.Struct, *types.Interface: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) case *types.Array: return fmt.Sprintf("Go$arrayIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) default: return fmt.Sprintf("Go$pointerIsEqual(%s, %s)", c.translateExprToType(e.X, t), c.translateExprToType(e.Y, t)) } default: return c.translateExprToType(e.X, t) + " === " + c.translateExprToType(e.Y, t) } default: panic(e.Op) } case *ast.ParenExpr: return fmt.Sprintf("(%s)", c.translateExpr(e.X)) case *ast.IndexExpr: xType := c.info.Types[e.X] if ptr, isPointer := xType.(*types.Pointer); isPointer { xType = ptr.Elem() } switch t := xType.Underlying().(type) { case *types.Array: return fmt.Sprintf("%s[%s]", c.translateExpr(e.X), c.flatten64(e.Index)) case *types.Slice: sliceVar := c.newVariable("_slice") indexVar := c.newVariable("_index") return fmt.Sprintf(`(%s = %s, %s = %s, (%s >= 0 && %s < %s.length) ? %s.array[%s.offset + %s] : Go$throwRuntimeError("index out of range"))`, sliceVar, c.translateExpr(e.X), indexVar, c.flatten64(e.Index), indexVar, indexVar, sliceVar, sliceVar, sliceVar, indexVar) case *types.Map: key := c.makeKey(e.Index, t.Key()) if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? [Go$obj.v, true] : [%s, false])`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) } return fmt.Sprintf(`(Go$obj = (%s || false)[%s], Go$obj !== undefined ? Go$obj.v : %s)`, c.translateExpr(e.X), key, c.zeroValue(t.Elem())) case *types.Basic: return fmt.Sprintf("%s.charCodeAt(%s)", c.translateExpr(e.X), c.flatten64(e.Index)) default: panic(fmt.Sprintf("Unhandled IndexExpr: %T\n", t)) } case *ast.SliceExpr: b, isBasic := c.info.Types[e.X].(*types.Basic) isString := isBasic && b.Info()&types.IsString != 0 slice := c.translateExprToType(e.X, exprType) if e.High == nil { if e.Low == nil { return slice } if isString { return fmt.Sprintf("%s.substring(%s)", slice, c.flatten64(e.Low)) } return fmt.Sprintf("Go$subslice(%s, %s)", slice, c.flatten64(e.Low)) } low := "0" if e.Low != nil { low = c.flatten64(e.Low) } if isString { return fmt.Sprintf("%s.substring(%s, %s)", slice, low, c.flatten64(e.High)) } if e.Max != nil { return fmt.Sprintf("Go$subslice(%s, %s, %s, %s)", slice, low, c.flatten64(e.High), c.flatten64(e.Max)) } return fmt.Sprintf("Go$subslice(%s, %s, %s)", slice, low, c.flatten64(e.High)) case *ast.SelectorExpr: sel := c.info.Selections[e] parameterName := func(v *types.Var) string { if v.Anonymous() || v.Name() == "" { return c.newVariable("param") } return c.newVariable(v.Name()) } makeParametersList := func() []string { params := sel.Obj().Type().(*types.Signature).Params() names := make([]string, params.Len()) for i := 0; i < params.Len(); i++ { names[i] = parameterName(params.At(i)) } return names } switch sel.Kind() { case types.FieldVal: return c.translateExpr(e.X) + "." + translateSelection(sel) case types.MethodVal: parameters := makeParametersList() recv := c.newVariable("_recv") return fmt.Sprintf("(%s = %s, function(%s) { return %s.%s(%s); })", recv, c.translateExpr(e.X), strings.Join(parameters, ", "), recv, e.Sel.Name, strings.Join(parameters, ", ")) case types.MethodExpr: recv := "recv" if isWrapped(sel.Recv()) { recv = fmt.Sprintf("(new %s(recv))", c.typeName(sel.Recv())) } parameters := makeParametersList() return fmt.Sprintf("(function(%s) { return %s.%s(%s); })", strings.Join(append([]string{"recv"}, parameters...), ", "), recv, sel.Obj().(*types.Func).Name(), strings.Join(parameters, ", ")) case types.PackageObj: return fmt.Sprintf("%s.%s", c.translateExpr(e.X), e.Sel.Name) } panic("") case *ast.CallExpr: plainFun := e.Fun for { if p, isParen := plainFun.(*ast.ParenExpr); isParen { plainFun = p.X continue } break } switch f := plainFun.(type) { case *ast.Ident: switch o := c.info.Objects[f].(type) { case *types.Builtin: switch o.Name() { case "new": t := c.info.Types[e].(*types.Pointer) if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Uintptr]) { return "new Uint8Array(8)" } switch t.Elem().Underlying().(type) { case *types.Struct, *types.Array: return c.zeroValue(t.Elem()) default: return fmt.Sprintf("Go$newDataPointer(%s, %s)", c.zeroValue(t.Elem()), c.typeName(t)) } case "make": switch t2 := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: if len(e.Args) == 3 { return fmt.Sprintf("Go$subslice(new %s(Go$makeArray(%s, %s, function() { return %s; })), 0, %s)", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[2], types.Typ[types.Int]), c.zeroValue(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int])) } return fmt.Sprintf("new %s(Go$makeArray(%s, %s, function() { return %s; }))", c.typeName(c.info.Types[e.Args[0]]), toArrayType(t2.Elem()), c.translateExprToType(e.Args[1], types.Typ[types.Int]), c.zeroValue(t2.Elem())) default: args := []string{"undefined"} for _, arg := range e.Args[1:] { args = append(args, c.translateExpr(arg)) } return fmt.Sprintf("new %s(%s)", c.typeName(c.info.Types[e.Args[0]]), strings.Join(args, ", ")) } case "len": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Basic, *types.Slice: return arg + ".length" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Map: return fmt.Sprintf("(Go$obj = %s, Go$obj !== null ? Go$keys(Go$obj).length : 0)", arg) case *types.Chan: return "0" // length of array is constant default: panic(fmt.Sprintf("Unhandled len type: %T\n", argType)) } case "cap": arg := c.translateExpr(e.Args[0]) switch argType := c.info.Types[e.Args[0]].Underlying().(type) { case *types.Slice: return arg + ".capacity" case *types.Pointer: return fmt.Sprintf("(%s, %d)", arg, argType.Elem().(*types.Array).Len()) case *types.Chan: return "0" // capacity of array is constant default: panic(fmt.Sprintf("Unhandled cap type: %T\n", argType)) } case "panic": return fmt.Sprintf("throw new Go$Panic(%s)", c.translateExprToType(e.Args[0], types.NewInterface(nil, nil))) case "append": if e.Ellipsis.IsValid() { return fmt.Sprintf("Go$append(%s, %s)", c.translateExpr(e.Args[0]), c.translateExprToType(e.Args[1], exprType)) } sliceType := exprType.Underlying().(*types.Slice) toAppend := createListComposite(sliceType.Elem(), c.translateExprSlice(e.Args[1:], sliceType.Elem())) return fmt.Sprintf("Go$append(%s, new %s(%s))", c.translateExpr(e.Args[0]), c.typeName(exprType), toAppend) case "delete": return fmt.Sprintf(`delete (%s || Go$Map.Go$nil)[%s]`, c.translateExpr(e.Args[0]), c.makeKey(e.Args[1], c.info.Types[e.Args[0]].Underlying().(*types.Map).Key())) case "copy": return fmt.Sprintf("Go$copy(%s, %s)", c.translateExprToType(e.Args[0], types.NewSlice(types.Typ[types.Byte])), c.translateExprToType(e.Args[1], types.NewSlice(types.Typ[types.Byte]))) case "print", "println": return fmt.Sprintf("console.log(%s)", strings.Join(c.translateExprSlice(e.Args, nil), ", ")) case "complex": return fmt.Sprintf("new %s(%s, %s)", c.typeName(c.info.Types[e]), c.translateExpr(e.Args[0]), c.translateExpr(e.Args[1])) case "real": return c.translateExpr(e.Args[0]) + ".real" case "imag": return c.translateExpr(e.Args[0]) + ".imag" case "recover": return "Go$recover()" case "close": return `Go$throwRuntimeError("not supported by GopherJS: close")` default: panic(fmt.Sprintf("Unhandled builtin: %s\n", o.Name())) } case *types.TypeName: // conversion if basic, isBasic := o.Type().Underlying().(*types.Basic); isBasic && !types.IsIdentical(c.info.Types[e.Args[0]], types.Typ[types.UnsafePointer]) { return fixNumber(c.translateExprToType(e.Args[0], o.Type()), basic) } return c.translateExprToType(e.Args[0], o.Type()) } case *ast.FuncType: // conversion return c.translateExprToType(e.Args[0], c.info.Types[plainFun]) } funType := c.info.Types[plainFun] sig, isSig := funType.Underlying().(*types.Signature) if !isSig { // conversion if c.pkg.Path() == "reflect" { if call, isCall := e.Args[0].(*ast.CallExpr); isCall && types.IsIdentical(c.info.Types[call.Fun], types.Typ[types.UnsafePointer]) { if named, isNamed := funType.(*types.Pointer).Elem().(*types.Named); isNamed { return c.translateExpr(call.Args[0]) + "." + named.Obj().Name() // unsafe conversion } } } return c.translateExprToType(e.Args[0], funType) } var fun string switch f := plainFun.(type) { case *ast.SelectorExpr: sel := c.info.Selections[f] switch sel.Kind() { case types.MethodVal: methodsRecvType := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type() _, pointerExpected := methodsRecvType.(*types.Pointer) _, isPointer := sel.Recv().Underlying().(*types.Pointer) _, isStruct := sel.Recv().Underlying().(*types.Struct) _, isArray := sel.Recv().Underlying().(*types.Array) if pointerExpected && !isPointer && !isStruct && !isArray { target := c.translateExpr(f.X) vVar := c.newVariable("v") fun = fmt.Sprintf("(new %s(function() { return %s; }, function(%s) { %s = %s; })).%s", c.typeName(methodsRecvType), target, vVar, target, vVar, f.Sel.Name) break } if isWrapped(sel.Recv()) { fun = fmt.Sprintf("(new %s(%s)).%s", c.typeName(sel.Recv()), c.translateExpr(f.X), f.Sel.Name) break } fun = fmt.Sprintf("%s.%s", c.translateExpr(f.X), f.Sel.Name) case types.FieldVal, types.MethodExpr, types.PackageObj: fun = c.translateExpr(f) default: panic("") } default: fun = c.translateExpr(plainFun) } if len(e.Args) == 1 { if tuple, isTuple := c.info.Types[e.Args[0]].(*types.Tuple); isTuple { args := make([]ast.Expr, tuple.Len()) for i := range args { args[i] = c.newIdent(fmt.Sprintf("Go$tuple[%d]", i), tuple.At(i).Type()) } return fmt.Sprintf("(Go$tuple = %s, %s(%s))", c.translateExpr(e.Args[0]), fun, c.translateArgs(sig, args, false)) } } return fmt.Sprintf("%s(%s)", fun, c.translateArgs(sig, e.Args, e.Ellipsis.IsValid())) case *ast.StarExpr: if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 { if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.IsIdentical(c.info.Types[c2.Fun], types.Typ[types.UnsafePointer]) { if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND { return c.translateExpr(unary.X) // unsafe conversion } } } switch exprType.Underlying().(type) { case *types.Struct, *types.Array: return c.translateExpr(e.X) } return c.translateExpr(e.X) + ".Go$get()" case *ast.TypeAssertExpr: if e.Type == nil { return c.translateExpr(e.X) } t := c.info.Types[e.Type] check := "Go$obj !== null && " + c.typeCheck("Go$obj.constructor", t) value := "Go$obj" if _, isInterface := t.Underlying().(*types.Interface); !isInterface { value += ".Go$val" } if _, isTuple := exprType.(*types.Tuple); isTuple { return fmt.Sprintf("(Go$obj = %s, %s ? [%s, true] : [%s, false])", c.translateExpr(e.X), check, value, c.zeroValue(c.info.Types[e.Type])) } return fmt.Sprintf("(Go$obj = %s, %s ? %s : Go$typeAssertionFailed(Go$obj))", c.translateExpr(e.X), check, value) case *ast.Ident: if e.Name == "_" { panic("Tried to translate underscore identifier.") } switch o := c.info.Objects[e].(type) { case *types.PkgName: return c.pkgVars[o.Pkg().Path()] case *types.Var, *types.Const: return c.objectName(o) case *types.Func: return c.objectName(o) case *types.TypeName: return c.typeName(o.Type()) case *types.Nil: return c.zeroValue(c.info.Types[e]) case nil: return e.Name default: panic(fmt.Sprintf("Unhandled object: %T\n", o)) } case nil: return "" default: panic(fmt.Sprintf("Unhandled expression: %T\n", e)) } }
// Ensure that, in debug mode, we can determine the ssa.Value // corresponding to every ast.Expr. func TestValueForExpr(t *testing.T) { imp := importer.New(new(importer.Config)) // (uses GCImporter) f, err := parser.ParseFile(imp.Fset, "testdata/valueforexpr.go", nil, parser.ParseComments) if err != nil { t.Error(err) return } mainInfo := imp.CreatePackage("main", f) prog := ssa.NewProgram(imp.Fset, 0) if err := prog.CreatePackages(imp); err != nil { t.Error(err) return } 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.DumpTo(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 := imp.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, _ := importer.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.IsIdentical(T, mainInfo.TypeOf(e)) { t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T) } } } }
func (c *PkgContext) translateExprToType(expr ast.Expr, desiredType types.Type) string { if desiredType == nil { return c.translateExpr(expr) } if expr == nil { return c.zeroValue(desiredType) } exprType := c.info.Types[expr] // TODO should be fixed in go/types if _, isSlice := exprType.(*types.Slice); isSlice { constValue := c.info.Values[expr] if constValue != nil && constValue.Kind() == exact.String { exprType = types.Typ[types.String] c.info.Types[expr] = exprType } } basicExprType, isBasicExpr := exprType.Underlying().(*types.Basic) if isBasicExpr && basicExprType.Kind() == types.UntypedNil { return c.zeroValue(desiredType) } switch t := desiredType.Underlying().(type) { case *types.Basic: switch { case t.Info()&types.IsInteger != 0: switch { case is64Bit(t): switch { case !is64Bit(basicExprType): return fmt.Sprintf("new %s(0, %s)", c.typeName(desiredType), c.translateExpr(expr)) case !types.IsIdentical(exprType, desiredType): return fmt.Sprintf("(Go$obj = %s, new %s(Go$obj.high, Go$obj.low))", c.translateExpr(expr), c.typeName(desiredType)) } case is64Bit(basicExprType): return fmt.Sprintf("(Go$obj = %s, Go$obj.low + ((Go$obj.high >> 31) * 4294967296))", c.translateExpr(expr)) case basicExprType.Info()&types.IsFloat != 0: return fmt.Sprintf("(%s >> 0)", c.translateExpr(expr)) default: return c.translateExpr(expr) } case t.Info()&types.IsFloat != 0: return c.flatten64(expr) case t.Info()&types.IsString != 0: value := c.translateExpr(expr) switch et := exprType.Underlying().(type) { case *types.Basic: if is64Bit(et) { value = fmt.Sprintf("%s.low", value) } if et.Info()&types.IsNumeric != 0 { return fmt.Sprintf("Go$encodeRune(%s)", value) } return value case *types.Slice: if types.IsIdentical(et.Elem().Underlying(), types.Typ[types.Rune]) { return fmt.Sprintf("Go$runesToString(%s)", value) } return fmt.Sprintf("Go$bytesToString(%s)", value) default: panic(fmt.Sprintf("Unhandled conversion: %v\n", et)) } case t.Kind() == types.UnsafePointer: if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND { if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr { return fmt.Sprintf("Go$sliceToArray(%s)", c.translateExprToType(indexExpr.X, types.NewSlice(nil))) } if ident, isIdent := unary.X.(*ast.Ident); isIdent && ident.Name == "_zero" { return "new Uint8Array(0)" } } if ptr, isPtr := c.info.Types[expr].(*types.Pointer); isPtr { if s, isStruct := ptr.Elem().Underlying().(*types.Struct); isStruct { array := c.newVariable("_array") target := c.newVariable("_struct") c.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s)) c.Delayed(func() { c.Printf("%s = %s;", target, c.translateExpr(expr)) c.loadStruct(array, target, s) }) return array } } } case *types.Slice: switch et := exprType.Underlying().(type) { case *types.Basic: if et.Info()&types.IsString != 0 { if types.IsIdentical(t.Elem().Underlying(), types.Typ[types.Rune]) { return fmt.Sprintf("new %s(Go$stringToRunes(%s))", c.typeName(desiredType), c.translateExpr(expr)) } return fmt.Sprintf("new %s(Go$stringToBytes(%s))", c.typeName(desiredType), c.translateExpr(expr)) } case *types.Array, *types.Pointer: return fmt.Sprintf("new %s(%s)", c.typeName(desiredType), c.translateExpr(expr)) } _, desiredIsNamed := desiredType.(*types.Named) if desiredIsNamed && !types.IsIdentical(exprType, desiredType) { return fmt.Sprintf("(Go$obj = %s, Go$subslice(new %s(Go$obj.array), Go$obj.offset, Go$obj.offset + Go$obj.length))", c.translateExpr(expr), c.typeName(desiredType)) } return c.translateExpr(expr) case *types.Interface: if isWrapped(exprType) { return fmt.Sprintf("new %s(%s)", c.typeName(exprType), c.translateExpr(expr)) } if _, isStruct := exprType.Underlying().(*types.Struct); isStruct { return fmt.Sprintf("(Go$obj = %s, new Go$obj.constructor.Go$NonPointer(Go$obj))", c.translateExpr(expr)) } case *types.Pointer: n, isNamed := t.Elem().(*types.Named) s, isStruct := t.Elem().Underlying().(*types.Struct) if isStruct && types.IsIdentical(exprType, types.Typ[types.UnsafePointer]) { array := c.newVariable("_array") target := c.newVariable("_struct") c.Printf("%s = %s;", array, c.translateExpr(expr)) c.Printf("%s = %s;", target, c.zeroValue(t.Elem())) c.loadStruct(array, target, s) return target } if isNamed && !types.IsIdentical(exprType, desiredType) { if isStruct { return c.clone(c.translateExpr(expr), t.Elem()) } return fmt.Sprintf("(Go$obj = %s, new %s.Go$Pointer(Go$obj.Go$get, Go$obj.Go$set))", c.translateExpr(expr), c.typeName(n)) } case *types.Struct, *types.Array: if _, isComposite := expr.(*ast.CompositeLit); !isComposite { return c.clone(c.translateExpr(expr), desiredType) } case *types.Chan, *types.Map, *types.Signature: // no converion default: panic(fmt.Sprintf("Unhandled conversion: %v\n", t)) } return c.translateExpr(expr) }
// matchArgTypeInternal is the internal version of matchArgType. It carries a map // remembering what types are in progress so we don't recur when faced with recursive // types or mutually recursive types. func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool { // %v, %T accept any argument type. if t == anyType { return true } if typ == nil { // external call typ = f.pkg.types[arg] if typ == nil { return true // probably a type check problem } } // If the type implements fmt.Formatter, we have nothing to check. // But (see issue 6259) that's not easy to verify, so instead we see // if its method set contains a Format function. We could do better, // even now, but we don't need to be 100% accurate. Wait for 6259 to // be fixed instead. TODO. if hasMethod(typ, "Format") { return true } // If we can use a string, might arg (dynamically) implement the Stringer or Error interface? if t&argString != 0 { if types.Implements(typ, errorType, false) || types.Implements(typ, stringerType, false) { return true } } typ = typ.Underlying() if inProgress[typ] { // We're already looking at this type. The call that started it will take care of it. return true } inProgress[typ] = true switch typ := typ.(type) { case *types.Signature: return t&argPointer != 0 case *types.Map: // Recur: map[int]int matches %d. return t&argPointer != 0 || (f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)) case *types.Chan: return t&argPointer != 0 case *types.Array: // Same as slice. if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { return true // %s matches []byte } // Recur: []int matches %d. return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress) case *types.Slice: // Same as array. if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { return true // %s matches []byte } // Recur: []int matches %d. But watch out for // type T []T // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below. return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress) case *types.Pointer: // Ugly, but dealing with an edge case: a known pointer to an invalid type, // probably something from a failed import. if typ.Elem().String() == "invalid type" { if *verbose { f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg)) } return true // special case } // If it's actually a pointer with %p, it prints as one. if t == argPointer { return true } // If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct. if str, ok := typ.Elem().Underlying().(*types.Struct); ok { return f.matchStructArgType(t, str, arg, inProgress) } // The rest can print with %p as pointers, or as integers with %x etc. return t&(argInt|argPointer) != 0 case *types.Struct: return f.matchStructArgType(t, typ, arg, inProgress) case *types.Interface: // If the static type of the argument is empty interface, there's little we can do. // Example: // func f(x interface{}) { fmt.Printf("%s", x) } // Whether x is valid for %s depends on the type of the argument to f. One day // we will be able to do better. For now, we assume that empty interface is OK // but non-empty interfaces, with Stringer and Error handled above, are errors. return typ.NumMethods() == 0 case *types.Basic: switch typ.Kind() { case types.UntypedBool, types.Bool: return t&argBool != 0 case types.UntypedInt, types.Int, types.Int8, types.Int16, types.Int32, types.Int64, types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: return t&argInt != 0 case types.UntypedFloat, types.Float32, types.Float64: return t&argFloat != 0 case types.UntypedComplex, types.Complex64, types.Complex128: return t&argComplex != 0 case types.UntypedString, types.String: return t&argString != 0 case types.UnsafePointer: return t&(argPointer|argInt) != 0 case types.UntypedRune: return t&(argInt|argRune) != 0 case types.UntypedNil: return t&argPointer != 0 // TODO? case types.Invalid: if *verbose { f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg)) } return true // Probably a type check problem. } panic("unreachable") } return false }
// 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}. // 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) { arrowPos := findArrow(qpos) if arrowPos == token.NoPos { return nil, fmt.Errorf("there is no send/receive here") } buildSSA(o) var queryOp chanOp // the originating send or receive operation var ops []chanOp // all sends/receives of opposite direction // Look at all send/receive instructions in the whole ssa.Program. // Build a list of those of same type to 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 == arrowPos { 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.IsIdentical(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) // Combine the PT sets from all contexts. queryChanPts := pointer.PointsToCombined(ptares.Queries[queryOp.ch]) // Ascertain which make(chan) labels the query's channel can alias. var makes []token.Pos for _, label := range queryChanPts.Labels() { makes = append(makes, label.Pos()) } sort.Sort(byPos(makes)) // Ascertain which send/receive operations can alias the same make(chan) labels. var sends, receives []token.Pos for _, op := range ops { for _, ptr := range ptares.Queries[op.ch] { if ptr.PointsTo().Intersects(queryChanPts) { if op.dir == types.SendOnly { sends = append(sends, op.pos) } else { receives = append(receives, op.pos) } } } } sort.Sort(byPos(sends)) sort.Sort(byPos(receives)) return &peersResult{ queryPos: arrowPos, queryType: queryType, makes: makes, sends: sends, receives: receives, }, nil }