// zeroConst returns a new "zero" constant of the specified type, // which must not be an array or struct type: the zero values of // aggregates are well-defined but cannot be represented by Const. // func zeroConst(t types.Type) *Const { switch t := t.(type) { case *types.Basic: switch { case t.Info()&types.IsBoolean != 0: return NewConst(exact.MakeBool(false), t) case t.Info()&types.IsNumeric != 0: return NewConst(exact.MakeInt64(0), t) case t.Info()&types.IsString != 0: return NewConst(exact.MakeString(""), t) case t.Kind() == types.UnsafePointer: fallthrough case t.Kind() == types.UntypedNil: return nilConst(t) default: panic(fmt.Sprint("zeroConst for unexpected type:", t)) } case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: return nilConst(t) case *types.Named: return NewConst(zeroConst(t.Underlying()).Value, t) case *types.Array, *types.Struct, *types.Tuple: panic(fmt.Sprint("zeroConst applied to aggregate:", t)) } panic(fmt.Sprint("zeroConst: unexpected ", t)) }
func (x array) hash(t types.Type) int { h := 0 tElt := t.Underlying().(*types.Array).Elem() for _, xi := range x { h += hash(tElt, xi) } return h }
// Reads the value from the given interface type, assuming that the // interface holds a value of the correct type. func (fr *frame) getInterfaceValue(v *govalue, ty types.Type) *govalue { val := fr.builder.CreateExtractValue(v.value, 1, "") if _, ok := ty.Underlying().(*types.Pointer); !ok { typedval := fr.builder.CreateBitCast(val, llvm.PointerType(fr.types.ToLLVM(ty), 0), "") val = fr.builder.CreateLoad(typedval, "") } return newValue(val, ty) }
func (fr *frame) makeInterface(llv llvm.Value, vty types.Type, iface types.Type) *govalue { if _, ok := vty.Underlying().(*types.Pointer); !ok { ptr := fr.createTypeMalloc(vty) fr.builder.CreateStore(llv, ptr) llv = ptr } return fr.makeInterfaceFromPointer(llv, vty, iface) }
func (fr *frame) makeInterfaceFromPointer(vptr llvm.Value, vty types.Type, iface types.Type) *govalue { i8ptr := llvm.PointerType(llvm.Int8Type(), 0) llv := fr.builder.CreateBitCast(vptr, i8ptr, "") value := llvm.Undef(fr.types.ToLLVM(iface)) itab := fr.types.getItabPointer(vty, iface.Underlying().(*types.Interface)) value = fr.builder.CreateInsertValue(value, itab, 0, "") value = fr.builder.CreateInsertValue(value, llv, 1, "") return newValue(value, iface) }
// If cond is true, reads the value from the given interface type, otherwise // returns a nil value. func (fr *frame) getInterfaceValueOrNull(cond llvm.Value, v *govalue, ty types.Type) *govalue { val := fr.builder.CreateExtractValue(v.value, 1, "") if _, ok := ty.Underlying().(*types.Pointer); ok { val = fr.builder.CreateSelect(cond, val, llvm.ConstNull(val.Type()), "") } else { val = fr.loadOrNull(cond, val, ty).value } return newValue(val, ty) }
func (x array) eq(t types.Type, _y interface{}) bool { y := _y.(array) tElt := t.Underlying().(*types.Array).Elem() for i, xi := range x { if !equals(tElt, xi, y[i]) { return false } } return true }
func (x structure) hash(t types.Type) int { tStruct := t.Underlying().(*types.Struct) h := 0 for i, n := 0, tStruct.NumFields(); i < n; i++ { if f := tStruct.Field(i); !f.Anonymous() { h += hash(f.Type(), x[i]) } } return h }
// usesBuiltinMap returns true if the built-in hash function and // equivalence relation for type t are consistent with those of the // interpreter's representation of type t. Such types are: all basic // types (bool, numbers, string), pointers and channels. // // usesBuiltinMap returns false for types that require a custom map // implementation: interfaces, arrays and structs. // // Panic ensues if t is an invalid map key type: function, map or slice. func usesBuiltinMap(t types.Type) bool { switch t := t.(type) { case *types.Basic, *types.Chan, *types.Pointer: return true case *types.Named: return usesBuiltinMap(t.Underlying()) case *types.Interface, *types.Array, *types.Struct: return false } panic(fmt.Sprintf("invalid map key type: %T", t)) }
func (x structure) eq(t types.Type, _y interface{}) bool { y := _y.(structure) tStruct := t.Underlying().(*types.Struct) for i, n := 0, tStruct.NumFields(); i < n; i++ { if f := tStruct.Field(i); !f.Anonymous() { if !equals(f.Type(), x[i], y[i]) { return false } } } return true }
// CanHaveDynamicTypes reports whether the type T can "hold" dynamic types, // i.e. is an interface (incl. reflect.Type) or a reflect.Value. // func CanHaveDynamicTypes(T types.Type) bool { switch T := T.(type) { case *types.Named: if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { return true // reflect.Value } return CanHaveDynamicTypes(T.Underlying()) case *types.Interface: return true } return false }
func (fr *frame) interfaceTypeAssert(val *govalue, ty types.Type) *govalue { if _, ok := ty.Underlying().(*types.Interface); ok { return fr.changeInterface(val, ty, true) } else { valtytd := fr.types.ToRuntime(val.Type()) valtd := fr.getInterfaceTypeDescriptor(val) tytd := fr.types.ToRuntime(ty) fr.runtime.checkInterfaceType.call(fr, valtd, tytd, valtytd) return fr.getInterfaceValue(val, ty) } }
// CanPoint reports whether the type T is pointerlike, // for the purposes of this analysis. func CanPoint(T types.Type) bool { switch T := T.(type) { case *types.Named: if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" { return true // treat reflect.Value like interface{} } return CanPoint(T.Underlying()) case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice: return true } return false // array struct tuple builtin basic }
// eqnil returns the comparison x == y using the equivalence relation // appropriate for type t. // If t is a reference type, at most one of x or y may be a nil value // of that type. // func eqnil(t types.Type, x, y value) bool { switch t.Underlying().(type) { case *types.Map, *types.Signature, *types.Slice: // Since these types don't support comparison, // one of the operands must be a literal nil. switch x := x.(type) { case *hashmap: return (x != nil) == (y.(*hashmap) != nil) case map[value]value: return (x != nil) == (y.(map[value]value) != nil) case *ssa.Function: switch y := y.(type) { case *ssa.Function: return (x != nil) == (y != nil) case *closure: return true } case *closure: return (x != nil) == (y.(*ssa.Function) != nil) case []value: return (x != nil) == (y.([]value) != nil) } panic(fmt.Sprintf("eqnil(%s): illegal dynamic type: %T", t, x)) } return equals(t, x, y) }
// zeroValue emits to f code to produce a zero value of type t, // and returns it. // func zeroValue(f *Function, t types.Type) Value { switch t.Underlying().(type) { case *types.Struct, *types.Array: return emitLoad(f, f.addLocal(t, token.NoPos)) default: return zeroConst(t) } }
// IntuitiveMethodSet returns the intuitive method set of a type, T. // // The result contains MethodSet(T) and additionally, if T is a // concrete type, methods belonging to *T if there is no identically // named method on T itself. This corresponds to user intuition about // method sets; this function is intended only for user interfaces. // // The order of the result is as for types.MethodSet(T). // func IntuitiveMethodSet(T types.Type, msets *types.MethodSetCache) []*types.Selection { var result []*types.Selection mset := msets.MethodSet(T) if _, ok := T.Underlying().(*types.Interface); ok { for i, n := 0, mset.Len(); i < n; i++ { result = append(result, mset.At(i)) } } else { pmset := msets.MethodSet(types.NewPointer(T)) for i, n := 0, pmset.Len(); i < n; i++ { meth := pmset.At(i) if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil { meth = m } result = append(result, meth) } } return result }
func (fr *frame) interfaceTypeCheck(val *govalue, ty types.Type) (v *govalue, okval *govalue) { tytd := fr.types.ToRuntime(ty) if _, ok := ty.Underlying().(*types.Interface); ok { var result []llvm.Value if val.Type().Underlying().(*types.Interface).NumMethods() > 0 { result = fr.runtime.ifaceI2I2.call(fr, tytd, val.value) } else { result = fr.runtime.ifaceE2I2.call(fr, tytd, val.value) } v = newValue(result[0], ty) okval = newValue(result[1], types.Typ[types.Bool]) } else { valtd := fr.getInterfaceTypeDescriptor(val) tyequal := fr.runtime.typeDescriptorsEqual.call(fr, valtd, tytd)[0] okval = newValue(tyequal, types.Typ[types.Bool]) tyequal = fr.builder.CreateTrunc(tyequal, llvm.Int1Type(), "") v = fr.getInterfaceValueOrNull(tyequal, val, ty) } return }
// offsetOf returns the (abstract) offset of field index within struct // or tuple typ. func (a *analysis) offsetOf(typ types.Type, index int) uint32 { var offset uint32 switch t := typ.Underlying().(type) { case *types.Tuple: for i := 0; i < index; i++ { offset += a.sizeof(t.At(i).Type()) } case *types.Struct: offset++ // the node for the struct itself for i := 0; i < index; i++ { offset += a.sizeof(t.Field(i).Type()) } default: panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ)) } return offset }
// store stores value v of type T into *addr. func store(T types.Type, addr *value, v value) { switch T := T.Underlying().(type) { case *types.Struct: lhs := (*addr).(structure) rhs := v.(structure) for i := range lhs { store(T.Field(i).Type(), &lhs[i], rhs[i]) } case *types.Array: lhs := (*addr).(array) rhs := v.(array) for i := range lhs { store(T.Elem(), &lhs[i], rhs[i]) } default: *addr = v } }
// load returns the value of type T in *addr. func load(T types.Type, addr *value) value { switch T := T.Underlying().(type) { case *types.Struct: v := (*addr).(structure) a := make(structure, len(v)) for i := range a { a[i] = load(T.Field(i).Type(), &v[i]) } return a case *types.Array: v := (*addr).(array) a := make(array, len(v)) for i := range a { a[i] = load(T.Elem(), &v[i]) } return a default: return *addr } }
// zero returns a new "zero" value of the specified type. func zero(t types.Type) value { switch t := t.(type) { case *types.Basic: if t.Kind() == types.UntypedNil { panic("untyped nil has no zero value") } if t.Info()&types.IsUntyped != 0 { // TODO(adonovan): make it an invariant that // this is unreachable. Currently some // constants have 'untyped' types when they // should be defaulted by the typechecker. t = ssa.DefaultType(t).(*types.Basic) } switch t.Kind() { case types.Bool: return false case types.Int: return int(0) case types.Int8: return int8(0) case types.Int16: return int16(0) case types.Int32: return int32(0) case types.Int64: return int64(0) case types.Uint: return uint(0) case types.Uint8: return uint8(0) case types.Uint16: return uint16(0) case types.Uint32: return uint32(0) case types.Uint64: return uint64(0) case types.Uintptr: return uintptr(0) case types.Float32: return float32(0) case types.Float64: return float64(0) case types.Complex64: return complex64(0) case types.Complex128: return complex128(0) case types.String: return "" case types.UnsafePointer: return unsafe.Pointer(nil) default: panic(fmt.Sprint("zero for unexpected type:", t)) } case *types.Pointer: return (*value)(nil) case *types.Array: a := make(array, t.Len()) for i := range a { a[i] = zero(t.Elem()) } return a case *types.Named: return zero(t.Underlying()) case *types.Interface: return iface{} // nil type, methodset and value case *types.Slice: return []value(nil) case *types.Struct: s := make(structure, t.NumFields()) for i := range s { s[i] = zero(t.Field(i).Type()) } return s case *types.Tuple: if t.Len() == 1 { return zero(t.At(0).Type()) } s := make(tuple, t.Len()) for i := range s { s[i] = zero(t.At(i).Type()) } return s case *types.Chan: return chan value(nil) case *types.Map: if usesBuiltinMap(t.Key()) { return map[value]value(nil) } return (*hashmap)(nil) case *types.Signature: return (*ssa.Function)(nil) } panic(fmt.Sprint("zero: unexpected ", t)) }
func (tm *llvmTypeMap) getBackendType(t types.Type) backendType { switch t := t.(type) { case *types.Named: return tm.getBackendType(t.Underlying()) case *types.Basic: switch t.Kind() { case types.Bool, types.Uint8: return &intBType{1, false} case types.Int8: return &intBType{1, true} case types.Uint16: return &intBType{2, false} case types.Int16: return &intBType{2, true} case types.Uint32: return &intBType{4, false} case types.Int32: return &intBType{4, true} case types.Uint64: return &intBType{8, false} case types.Int64: return &intBType{8, true} case types.Uint, types.Uintptr: return &intBType{tm.target.PointerSize(), false} case types.Int: return &intBType{tm.target.PointerSize(), true} case types.Float32: return &floatBType{false} case types.Float64: return &floatBType{true} case types.UnsafePointer: return &ptrBType{} case types.Complex64: f32 := &floatBType{false} return &structBType{[]backendType{f32, f32}} case types.Complex128: f64 := &floatBType{true} return &structBType{[]backendType{f64, f64}} case types.String: return &structBType{[]backendType{&ptrBType{}, &intBType{tm.target.PointerSize(), false}}} } case *types.Struct: var fields []backendType for i := 0; i != t.NumFields(); i++ { f := t.Field(i) fields = append(fields, tm.getBackendType(f.Type())) } return &structBType{fields} case *types.Pointer, *types.Signature, *types.Map, *types.Chan: return &ptrBType{} case *types.Interface: i8ptr := &ptrBType{} return &structBType{[]backendType{i8ptr, i8ptr}} case *types.Slice: return tm.sliceBackendType() case *types.Array: return &arrayBType{uint64(t.Len()), tm.getBackendType(t.Elem())} } panic("unhandled type: " + t.String()) }
// isPointer returns true for types whose underlying type is a pointer. func isPointer(typ types.Type) bool { _, ok := typ.Underlying().(*types.Pointer) return ok }
// flatten returns a list of directly contained fields in the preorder // traversal of the type tree of t. The resulting elements are all // scalars (basic types or pointerlike types), except for struct/array // "identity" nodes, whose type is that of the aggregate. // // reflect.Value is considered pointerlike, similar to interface{}. // // Callers must not mutate the result. // func (a *analysis) flatten(t types.Type) []*fieldInfo { fl, ok := a.flattenMemo[t] if !ok { switch t := t.(type) { case *types.Named: u := t.Underlying() if isInterface(u) { // Debuggability hack: don't remove // the named type from interfaces as // they're very verbose. fl = append(fl, &fieldInfo{typ: t}) } else { fl = a.flatten(u) } case *types.Basic, *types.Signature, *types.Chan, *types.Map, *types.Interface, *types.Slice, *types.Pointer: fl = append(fl, &fieldInfo{typ: t}) case *types.Array: fl = append(fl, &fieldInfo{typ: t}) // identity node for _, fi := range a.flatten(t.Elem()) { fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi}) } case *types.Struct: fl = append(fl, &fieldInfo{typ: t}) // identity node for i, n := 0, t.NumFields(); i < n; i++ { f := t.Field(i) for _, fi := range a.flatten(f.Type()) { fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi}) } } case *types.Tuple: // No identity node: tuples are never address-taken. n := t.Len() if n == 1 { // Don't add a fieldInfo link for singletons, // e.g. in params/results. fl = append(fl, a.flatten(t.At(0).Type())...) } else { for i := 0; i < n; i++ { f := t.At(i) for _, fi := range a.flatten(f.Type()) { fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi}) } } } default: panic(t) } a.flattenMemo[t] = fl } return fl }
func deref(t types.Type) types.Type { return t.Underlying().(*types.Pointer).Elem() }
func (fr *frame) slice(x llvm.Value, xtyp types.Type, low, high, max llvm.Value) llvm.Value { if !low.IsNil() { low = fr.createZExtOrTrunc(low, fr.types.inttype, "") } else { low = llvm.ConstNull(fr.types.inttype) } if !high.IsNil() { high = fr.createZExtOrTrunc(high, fr.types.inttype, "") } if !max.IsNil() { max = fr.createZExtOrTrunc(max, fr.types.inttype, "") } var arrayptr, arraylen, arraycap llvm.Value var elemtyp types.Type var errcode uint64 switch typ := xtyp.Underlying().(type) { case *types.Pointer: // *array errcode = gccgoRuntimeErrorARRAY_SLICE_OUT_OF_BOUNDS arraytyp := typ.Elem().Underlying().(*types.Array) elemtyp = arraytyp.Elem() arrayptr = x arrayptr = fr.builder.CreateBitCast(arrayptr, llvm.PointerType(llvm.Int8Type(), 0), "") arraylen = llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false) arraycap = arraylen case *types.Slice: errcode = gccgoRuntimeErrorSLICE_SLICE_OUT_OF_BOUNDS elemtyp = typ.Elem() arrayptr = fr.builder.CreateExtractValue(x, 0, "") arraylen = fr.builder.CreateExtractValue(x, 1, "") arraycap = fr.builder.CreateExtractValue(x, 2, "") case *types.Basic: if high.IsNil() { high = llvm.ConstAllOnes(fr.types.inttype) // -1 } result := fr.runtime.stringSlice.call(fr, x, low, high) return result[0] default: panic("unimplemented") } if high.IsNil() { high = arraylen } if max.IsNil() { max = arraycap } // Bounds checking: 0 <= low <= high <= max <= cap zero := llvm.ConstNull(fr.types.inttype) l0 := fr.builder.CreateICmp(llvm.IntSLT, low, zero, "") hl := fr.builder.CreateICmp(llvm.IntSLT, high, low, "") mh := fr.builder.CreateICmp(llvm.IntSLT, max, high, "") cm := fr.builder.CreateICmp(llvm.IntSLT, arraycap, max, "") cond := fr.builder.CreateOr(l0, hl, "") cond = fr.builder.CreateOr(cond, mh, "") cond = fr.builder.CreateOr(cond, cm, "") fr.condBrRuntimeError(cond, errcode) slicelen := fr.builder.CreateSub(high, low, "") slicecap := fr.builder.CreateSub(max, low, "") elemsize := llvm.ConstInt(fr.llvmtypes.inttype, uint64(fr.llvmtypes.Sizeof(elemtyp)), false) offset := fr.builder.CreateMul(low, elemsize, "") sliceptr := fr.builder.CreateInBoundsGEP(arrayptr, []llvm.Value{offset}, "") llslicetyp := fr.llvmtypes.sliceBackendType().ToLLVM(fr.llvmtypes.ctx) sliceValue := llvm.Undef(llslicetyp) sliceValue = fr.builder.CreateInsertValue(sliceValue, sliceptr, 0, "") sliceValue = fr.builder.CreateInsertValue(sliceValue, slicelen, 1, "") sliceValue = fr.builder.CreateInsertValue(sliceValue, slicecap, 2, "") return sliceValue }
// conv converts the value x of type t_src to type t_dst and returns // the result. // Possible cases are described with the ssa.Convert operator. // func conv(t_dst, t_src types.Type, x value) value { ut_src := t_src.Underlying() ut_dst := t_dst.Underlying() // Destination type is not an "untyped" type. if b, ok := ut_dst.(*types.Basic); ok && b.Info()&types.IsUntyped != 0 { panic("oops: conversion to 'untyped' type: " + b.String()) } // Nor is it an interface type. if _, ok := ut_dst.(*types.Interface); ok { if _, ok := ut_src.(*types.Interface); ok { panic("oops: Convert should be ChangeInterface") } else { panic("oops: Convert should be MakeInterface") } } // Remaining conversions: // + untyped string/number/bool constant to a specific // representation. // + conversions between non-complex numeric types. // + conversions between complex numeric types. // + integer/[]byte/[]rune -> string. // + string -> []byte/[]rune. // // All are treated the same: first we extract the value to the // widest representation (int64, uint64, float64, complex128, // or string), then we convert it to the desired type. switch ut_src := ut_src.(type) { case *types.Pointer: switch ut_dst := ut_dst.(type) { case *types.Basic: // *value to unsafe.Pointer? if ut_dst.Kind() == types.UnsafePointer { return unsafe.Pointer(x.(*value)) } } case *types.Slice: // []byte or []rune -> string // TODO(adonovan): fix: type B byte; conv([]B -> string). switch ut_src.Elem().(*types.Basic).Kind() { case types.Byte: x := x.([]value) b := make([]byte, 0, len(x)) for i := range x { b = append(b, x[i].(byte)) } return string(b) case types.Rune: x := x.([]value) r := make([]rune, 0, len(x)) for i := range x { r = append(r, x[i].(rune)) } return string(r) } case *types.Basic: x = widen(x) // integer -> string? // TODO(adonovan): fix: test integer -> named alias of string. if ut_src.Info()&types.IsInteger != 0 { if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind() == types.String { return string(asInt(x)) } } // string -> []rune, []byte or string? if s, ok := x.(string); ok { switch ut_dst := ut_dst.(type) { case *types.Slice: var res []value // TODO(adonovan): fix: test named alias of rune, byte. switch ut_dst.Elem().(*types.Basic).Kind() { case types.Rune: for _, r := range []rune(s) { res = append(res, r) } return res case types.Byte: for _, b := range []byte(s) { res = append(res, b) } return res } case *types.Basic: if ut_dst.Kind() == types.String { return x.(string) } } break // fail: no other conversions for string } // unsafe.Pointer -> *value if ut_src.Kind() == types.UnsafePointer { // TODO(adonovan): this is wrong and cannot // really be fixed with the current design. // // return (*value)(x.(unsafe.Pointer)) // creates a new pointer of a different // type but the underlying interface value // knows its "true" type and so cannot be // meaningfully used through the new pointer. // // To make this work, the interpreter needs to // simulate the memory layout of a real // compiled implementation. // // To at least preserve type-safety, we'll // just return the zero value of the // destination type. return zero(t_dst) } // Conversions between complex numeric types? if ut_src.Info()&types.IsComplex != 0 { switch ut_dst.(*types.Basic).Kind() { case types.Complex64: return complex64(x.(complex128)) case types.Complex128: return x.(complex128) } break // fail: no other conversions for complex } // Conversions between non-complex numeric types? if ut_src.Info()&types.IsNumeric != 0 { kind := ut_dst.(*types.Basic).Kind() switch x := x.(type) { case int64: // signed integer -> numeric? switch kind { case types.Int: return int(x) case types.Int8: return int8(x) case types.Int16: return int16(x) case types.Int32: return int32(x) case types.Int64: return int64(x) case types.Uint: return uint(x) case types.Uint8: return uint8(x) case types.Uint16: return uint16(x) case types.Uint32: return uint32(x) case types.Uint64: return uint64(x) case types.Uintptr: return uintptr(x) case types.Float32: return float32(x) case types.Float64: return float64(x) } case uint64: // unsigned integer -> numeric? switch kind { case types.Int: return int(x) case types.Int8: return int8(x) case types.Int16: return int16(x) case types.Int32: return int32(x) case types.Int64: return int64(x) case types.Uint: return uint(x) case types.Uint8: return uint8(x) case types.Uint16: return uint16(x) case types.Uint32: return uint32(x) case types.Uint64: return uint64(x) case types.Uintptr: return uintptr(x) case types.Float32: return float32(x) case types.Float64: return float64(x) } case float64: // floating point -> numeric? switch kind { case types.Int: return int(x) case types.Int8: return int8(x) case types.Int16: return int16(x) case types.Int32: return int32(x) case types.Int64: return int64(x) case types.Uint: return uint(x) case types.Uint8: return uint8(x) case types.Uint16: return uint16(x) case types.Uint32: return uint32(x) case types.Uint64: return uint64(x) case types.Uintptr: return uintptr(x) case types.Float32: return float32(x) case types.Float64: return float64(x) } } } } panic(fmt.Sprintf("unsupported conversion: %s -> %s, dynamic type %T", t_src, t_dst, x)) }
// sliceToArray returns the type representing the arrays to which // slice type slice points. func sliceToArray(slice types.Type) *types.Array { return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1) }
// deref returns a pointer's element type; otherwise it returns typ. func deref(typ types.Type) types.Type { if p, ok := typ.Underlying().(*types.Pointer); ok { return p.Elem() } return typ }
// mustDeref returns the element type of its argument, which must be a // pointer; panic ensues otherwise. func mustDeref(typ types.Type) types.Type { return typ.Underlying().(*types.Pointer).Elem() }