Browse Source

reflect: let reflect.Type be of interface type

This matches the main Go implementation and (among others) fixes a
compatibility issue with the encoding/json package. The encoding/json
package compares reflect.Type variables against nil, which does not work
as long as reflect.Type is of integer type.

This also adds a reflect.RawType() function (like reflect.Type()) that
makes it easier to avoid working with interfaces in the runtime package.
It is internal only, but exported to let the runtime package use it.

This change introduces a small code size increase when working with the
reflect package, but I've tried to keep it to a minimum. Most programs
that don't make extensive use of the reflect package (and don't use
package like fmt) should not be impacted by this.
pull/1736/head
Ayke van Laethem 4 years ago
committed by Ron Evans
parent
commit
c849bccb83
  1. 2
      interp/interpreter.go
  2. 2
      src/reflect/swapper.go
  3. 295
      src/reflect/type.go
  4. 69
      src/reflect/value.go
  5. 14
      src/runtime/hashmap.go
  6. 11
      src/runtime/interface.go

2
interp/interpreter.go

@ -277,7 +277,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
copy(dstBuf.buf[dst.offset():dst.offset()+nBytes], srcBuf.buf[src.offset():])
dstObj.buffer = dstBuf
mem.put(dst.index(), dstObj)
case callFn.name == "(reflect.Type).Elem":
case callFn.name == "(reflect.rawType).elem":
if r.debug {
fmt.Fprintln(os.Stderr, indent+"call (reflect.rawType).elem:", operands[1:])
}

2
src/reflect/swapper.go

@ -21,7 +21,7 @@ func Swapper(slice interface{}) func(i, j int) {
return func(i, j int) {}
}
typ := v.Type().Elem()
typ := v.typecode.Elem()
size := typ.Size()
header := (*SliceHeader)(v.value)

295
src/reflect/type.go

@ -122,30 +122,218 @@ func (k Kind) String() string {
}
// basicType returns a new Type for this kind if Kind is a basic type.
func (k Kind) basicType() Type {
return Type(k << 1)
func (k Kind) basicType() rawType {
return rawType(k << 1)
}
// The following Type type has been copied almost entirely from
// https://github.com/golang/go/blob/go1.15/src/reflect/type.go#L27-L212.
// Some methods have been commented out as they haven't yet been implemented.
// Type is the representation of a Go type.
//
// Not all methods apply to all kinds of types. Restrictions,
// if any, are noted in the documentation for each method.
// Use the Kind method to find out the kind of type before
// calling kind-specific methods. Calling a method
// inappropriate to the kind of type causes a run-time panic.
//
// Type values are comparable, such as with the == operator,
// so they can be used as map keys.
// Two Type values are equal if they represent identical types.
type Type interface {
// Methods applicable to all types.
// Align returns the alignment in bytes of a value of
// this type when allocated in memory.
Align() int
// FieldAlign returns the alignment in bytes of a value of
// this type when used as a field in a struct.
FieldAlign() int
// Method returns the i'th method in the type's method set.
// It panics if i is not in the range [0, NumMethod()).
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
//
// Only exported methods are accessible and they are sorted in
// lexicographic order.
//Method(int) Method
// MethodByName returns the method with that name in the type's
// method set and a boolean indicating if the method was found.
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
//MethodByName(string) (Method, bool)
// NumMethod returns the number of exported methods in the type's method set.
NumMethod() int
// Name returns the type's name within its package for a defined type.
// For other (non-defined) types it returns the empty string.
Name() string
// PkgPath returns a defined type's package path, that is, the import path
// that uniquely identifies the package, such as "encoding/base64".
// If the type was predeclared (string, error) or not defined (*T, struct{},
// []int, or A where A is an alias for a non-defined type), the package path
// will be the empty string.
//PkgPath() string
// Size returns the number of bytes needed to store
// a value of the given type; it is analogous to unsafe.Sizeof.
Size() uintptr
// String returns a string representation of the type.
// The string representation may use shortened package names
// (e.g., base64 instead of "encoding/base64") and is not
// guaranteed to be unique among types. To test for type identity,
// compare the Types directly.
String() string
// Kind returns the specific kind of this type.
Kind() Kind
// Implements reports whether the type implements the interface type u.
Implements(u Type) bool
// AssignableTo reports whether a value of the type is assignable to type u.
AssignableTo(u Type) bool
// ConvertibleTo reports whether a value of the type is convertible to type u.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
Comparable() bool
// Methods applicable only to some types, depending on Kind.
// The methods allowed for each kind are:
//
// Int*, Uint*, Float*, Complex*: Bits
// Array: Elem, Len
// Chan: ChanDir, Elem
// Func: In, NumIn, Out, NumOut, IsVariadic.
// Map: Key, Elem
// Ptr: Elem
// Slice: Elem
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
// Bits returns the size of the type in bits.
// It panics if the type's Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int
// ChanDir returns a channel type's direction.
// It panics if the type's Kind is not Chan.
//ChanDir() ChanDir
// IsVariadic reports whether a function type's final input parameter
// is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's
// implicit actual type []T.
//
// For concreteness, if t represents func(x int, y ... float64), then
//
// t.NumIn() == 2
// t.In(0) is the reflect.Type for "int"
// t.In(1) is the reflect.Type for "[]float64"
// t.IsVariadic() == true
//
// IsVariadic panics if the type's Kind is not Func.
//IsVariadic() bool
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
Elem() Type
// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField
// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
// It panics if the type's Kind is not Struct.
//FieldByIndex(index []int) StructField
// FieldByName returns the struct field with the given name
// and a boolean indicating if the field was found.
//FieldByName(name string) (StructField, bool)
// FieldByNameFunc returns the struct field with a name
// that satisfies the match function and a boolean indicating if
// the field was found.
//
// FieldByNameFunc considers the fields in the struct itself
// and then the fields in any embedded structs, in breadth first order,
// stopping at the shallowest nesting depth containing one or more
// fields satisfying the match function. If multiple fields at that depth
// satisfy the match function, they cancel each other
// and FieldByNameFunc returns no match.
// This behavior mirrors Go's handling of name lookup in
// structs containing embedded fields.
//FieldByNameFunc(match func(string) bool) (StructField, bool)
// In returns the type of a function type's i'th input parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumIn()).
//In(i int) Type
// Key returns a map type's key type.
// It panics if the type's Kind is not Map.
Key() Type
// Len returns an array type's length.
// It panics if the type's Kind is not Array.
Len() int
// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
NumField() int
// NumIn returns a function type's input parameter count.
// It panics if the type's Kind is not Func.
//NumIn() int
// NumOut returns a function type's output parameter count.
// It panics if the type's Kind is not Func.
//NumOut() int
// Out returns the type of a function type's i'th output parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumOut()).
//Out(i int) Type
}
// The typecode as used in an interface{}.
type Type uintptr
type rawType uintptr
func TypeOf(i interface{}) Type {
return ValueOf(i).typecode
}
func PtrTo(t Type) Type {
ptrType := t<<5 | 5 // 0b0101 == 5
ptrType := t.(rawType)<<5 | 5 // 0b0101 == 5
if ptrType>>5 != t {
panic("reflect: PtrTo type does not fit")
}
return ptrType
}
func (t Type) String() string {
func (t rawType) String() string {
return "T"
}
func (t Type) Kind() Kind {
func (t rawType) Kind() Kind {
if t%2 == 0 {
// basic type
return Kind((t >> 1) % 32)
@ -156,14 +344,18 @@ func (t Type) Kind() Kind {
// Elem returns the element type for channel, slice and array types, the
// pointed-to value for pointer types, and the key type for map types.
func (t Type) Elem() Type {
func (t rawType) Elem() Type {
return t.elem()
}
func (t rawType) elem() rawType {
switch t.Kind() {
case Chan, Ptr, Slice:
return t.stripPrefix()
case Array:
index := t.stripPrefix()
elem, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(index)))
return Type(elem)
return rawType(elem)
default: // not implemented: Map
panic("unimplemented: (reflect.Type).Elem()")
}
@ -175,14 +367,14 @@ func (t Type) Elem() Type {
// simply shifted off.
//
// The behavior is only defined for non-basic types.
func (t Type) stripPrefix() Type {
func (t rawType) stripPrefix() rawType {
// Look at the 'n' bit in the type code (see the top of this file) to see
// whether this is a named type.
if (t>>4)%2 != 0 {
// This is a named type. The data is stored in a sidetable.
namedTypeNum := t >> 5
n := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)*unsafe.Sizeof(uintptr(0))))
return Type(n)
return rawType(n)
}
// Not a named type, so the value is stored directly in the type code.
return t >> 5
@ -190,7 +382,23 @@ func (t Type) stripPrefix() Type {
// Field returns the type of the i'th field of this struct type. It panics if t
// is not a struct type.
func (t Type) Field(i int) StructField {
func (t rawType) Field(i int) StructField {
field := t.rawField(i)
return StructField{
Name: field.Name,
PkgPath: field.PkgPath,
Type: field.Type, // note: converts rawType to Type
Tag: field.Tag,
Anonymous: field.Anonymous,
Offset: field.Offset,
}
}
// rawField returns nearly the same value as Field but without converting the
// Type member to an interface.
//
// For internal use only.
func (t rawType) rawField(i int) rawStructField {
if t.Kind() != Struct {
panic(&TypeError{"Field"})
}
@ -206,7 +414,7 @@ func (t Type) Field(i int) StructField {
// efficient, but it is easy to implement.
// Adding a jump table at the start to jump to the field directly would
// make this much faster, but that would also impact code size.
field := StructField{}
field := rawStructField{}
offset := uintptr(0)
for fieldNum := 0; fieldNum <= i; fieldNum++ {
// Read some flags of this field, like whether the field is an
@ -215,15 +423,16 @@ func (t Type) Field(i int) StructField {
p = unsafe.Pointer(uintptr(p) + 1)
// Read the type of this struct field.
var fieldType uintptr
fieldType, p = readVarint(p)
field.Type = Type(fieldType)
var fieldTypeVal uintptr
fieldTypeVal, p = readVarint(p)
fieldType := rawType(fieldTypeVal)
field.Type = fieldType
// Move Offset forward to align it to this field's alignment.
// Assume alignment is a power of two.
offset = align(offset, uintptr(field.Type.Align()))
offset = align(offset, uintptr(fieldType.Align()))
field.Offset = offset
offset += field.Type.Size() // starting (unaligned) offset for next field
offset += fieldType.Size() // starting (unaligned) offset for next field
// Read the field name.
var nameNum uintptr
@ -264,7 +473,7 @@ func (t Type) Field(i int) StructField {
// Bits returns the number of bits that this type uses. It is only valid for
// arithmetic types (integers, floats, and complex numbers). For other types, it
// will panic.
func (t Type) Bits() int {
func (t rawType) Bits() int {
kind := t.Kind()
if kind >= Int && kind <= Complex128 {
return int(t.Size()) * 8
@ -274,7 +483,7 @@ func (t Type) Bits() int {
// Len returns the number of elements in this array. It panics of the type kind
// is not Array.
func (t Type) Len() int {
func (t rawType) Len() int {
if t.Kind() != Array {
panic(TypeError{"Len"})
}
@ -290,7 +499,7 @@ func (t Type) Len() int {
// NumField returns the number of fields of a struct type. It panics for other
// type kinds.
func (t Type) NumField() int {
func (t rawType) NumField() int {
if t.Kind() != Struct {
panic(&TypeError{"NumField"})
}
@ -301,7 +510,7 @@ func (t Type) NumField() int {
// Size returns the size in bytes of a given type. It is similar to
// unsafe.Sizeof.
func (t Type) Size() uintptr {
func (t rawType) Size() uintptr {
switch t.Kind() {
case Bool, Int8, Uint8:
return 1
@ -332,13 +541,13 @@ func (t Type) Size() uintptr {
case Interface:
return unsafe.Sizeof(interface{}(nil))
case Array:
return t.Elem().Size() * uintptr(t.Len())
return t.elem().Size() * uintptr(t.Len())
case Struct:
numField := t.NumField()
if numField == 0 {
return 0
}
lastField := t.Field(numField - 1)
lastField := t.rawField(numField - 1)
return lastField.Offset + lastField.Type.Size()
default:
panic("unimplemented: size of type")
@ -347,7 +556,7 @@ func (t Type) Size() uintptr {
// Align returns the alignment of this type. It is similar to calling
// unsafe.Alignof.
func (t Type) Align() int {
func (t rawType) Align() int {
switch t.Kind() {
case Bool, Int8, Uint8:
return int(unsafe.Alignof(int8(0)))
@ -381,14 +590,14 @@ func (t Type) Align() int {
numField := t.NumField()
alignment := 1
for i := 0; i < numField; i++ {
fieldAlignment := t.Field(i).Type.Align()
fieldAlignment := t.rawField(i).Type.Align()
if fieldAlignment > alignment {
alignment = fieldAlignment
}
}
return alignment
case Array:
return t.Elem().Align()
return t.elem().Align()
default:
panic("unimplemented: alignment of type")
}
@ -396,14 +605,14 @@ func (t Type) Align() int {
// FieldAlign returns the alignment if this type is used in a struct field. It
// is currently an alias for Align() but this might change in the future.
func (t Type) FieldAlign() int {
func (t rawType) FieldAlign() int {
return t.Align()
}
// AssignableTo returns whether a value of type u can be assigned to a variable
// of type t.
func (t Type) AssignableTo(u Type) bool {
if t == u {
func (t rawType) AssignableTo(u Type) bool {
if t == u.(rawType) {
return true
}
if t.Kind() == Interface {
@ -412,7 +621,7 @@ func (t Type) AssignableTo(u Type) bool {
return false
}
func (t Type) Implements(u Type) bool {
func (t rawType) Implements(u Type) bool {
if t.Kind() != Interface {
panic("reflect: non-interface type passed to Type.Implements")
}
@ -420,7 +629,7 @@ func (t Type) Implements(u Type) bool {
}
// Comparable returns whether values of this type can be compared to each other.
func (t Type) Comparable() bool {
func (t rawType) Comparable() bool {
switch t.Kind() {
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return true
@ -439,7 +648,7 @@ func (t Type) Comparable() bool {
case Slice:
return false
case Array:
return t.Elem().Comparable()
return t.elem().Comparable()
case Func:
return false
case Map:
@ -447,7 +656,7 @@ func (t Type) Comparable() bool {
case Struct:
numField := t.NumField()
for i := 0; i < numField; i++ {
if !t.Field(i).Type.Comparable() {
if !t.rawField(i).Type.Comparable() {
return false
}
}
@ -457,19 +666,19 @@ func (t Type) Comparable() bool {
}
}
func (t Type) ConvertibleTo(u Type) bool {
func (t rawType) ConvertibleTo(u Type) bool {
panic("unimplemented: (reflect.Type).ConvertibleTo()")
}
func (t Type) NumMethod() int {
func (t rawType) NumMethod() int {
panic("unimplemented: (reflect.Type).NumMethod()")
}
func (t Type) Name() string {
func (t rawType) Name() string {
panic("unimplemented: (reflect.Type).Name()")
}
func (t Type) Key() Type {
func (t rawType) Key() Type {
panic("unimplemented: (reflect.Type).Key()")
}
@ -488,6 +697,18 @@ type StructField struct {
Offset uintptr
}
// rawStructField is the same as StructField but with the Type member replaced
// with rawType. For internal use only. Avoiding this conversion to the Type
// interface improves code size in many cases.
type rawStructField struct {
Name string
PkgPath string
Type rawType
Tag StructTag
Anonymous bool
Offset uintptr
}
// A StructTag is the tag string in a struct field.
type StructTag string

69
src/reflect/value.go

@ -16,7 +16,7 @@ const (
)
type Value struct {
typecode Type
typecode rawType
value unsafe.Pointer
flags valueFlags
}
@ -36,10 +36,10 @@ func Indirect(v Value) Value {
}
//go:linkname composeInterface runtime.composeInterface
func composeInterface(Type, unsafe.Pointer) interface{}
func composeInterface(rawType, unsafe.Pointer) interface{}
//go:linkname decomposeInterface runtime.decomposeInterface
func decomposeInterface(i interface{}) (Type, unsafe.Pointer)
func decomposeInterface(i interface{}) (rawType, unsafe.Pointer)
func ValueOf(i interface{}) Value {
typecode, value := decomposeInterface(i)
@ -51,11 +51,11 @@ func ValueOf(i interface{}) Value {
}
func (v Value) Interface() interface{} {
if v.isIndirect() && v.Type().Size() <= unsafe.Sizeof(uintptr(0)) {
if v.isIndirect() && v.typecode.Size() <= unsafe.Sizeof(uintptr(0)) {
// Value was indirect but must be put back directly in the interface
// value.
var value uintptr
for j := v.Type().Size(); j != 0; j-- {
for j := v.typecode.Size(); j != 0; j-- {
value = (value << 8) | uintptr(*(*uint8)(unsafe.Pointer(uintptr(v.value) + j - 1)))
}
v.value = unsafe.Pointer(value)
@ -67,8 +67,16 @@ func (v Value) Type() Type {
return v.typecode
}
// Internal function only, do not use.
//
// RawType returns the raw, underlying type code. It is used in the runtime
// package and needs to be exported for the runtime package to access it.
func (v Value) RawType() rawType {
return v.typecode
}
func (v Value) Kind() Kind {
return v.Type().Kind()
return v.typecode.Kind()
}
// IsNil returns whether the value is the nil value. It panics if the value Kind
@ -313,10 +321,9 @@ func chanlen(p unsafe.Pointer) int
// Len returns the length of this value for slices, strings, arrays, channels,
// and maps. For other types, it panics.
func (v Value) Len() int {
t := v.Type()
switch t.Kind() {
switch v.typecode.Kind() {
case Array:
return v.Type().Len()
return v.typecode.Len()
case Chan:
return chanlen(v.value)
case Map:
@ -336,10 +343,9 @@ func chancap(p unsafe.Pointer) int
// Cap returns the capacity of this value for arrays, channels and slices.
// For other types, it panics.
func (v Value) Cap() int {
t := v.Type()
switch t.Kind() {
switch v.typecode.Kind() {
case Array:
return v.Type().Len()
return v.typecode.Len()
case Chan:
return chancap(v.value)
case Slice:
@ -352,7 +358,7 @@ func (v Value) Cap() int {
// NumField returns the number of fields of this struct. It panics for other
// value types.
func (v Value) NumField() int {
return v.Type().NumField()
return v.typecode.NumField()
}
func (v Value) Elem() Value {
@ -366,7 +372,7 @@ func (v Value) Elem() Value {
return Value{}
}
return Value{
typecode: v.Type().Elem(),
typecode: v.typecode.elem(),
value: ptr,
flags: v.flags | valueFlagIndirect,
}
@ -377,7 +383,7 @@ func (v Value) Elem() Value {
// Field returns the value of the i'th field of this struct.
func (v Value) Field(i int) Value {
structField := v.Type().Field(i)
structField := v.typecode.rawField(i)
flags := v.flags
if structField.PkgPath != "" {
// The fact that PkgPath is present means that this field is not
@ -385,14 +391,15 @@ func (v Value) Field(i int) Value {
flags &^= valueFlagExported
}
size := v.Type().Size()
fieldSize := structField.Type.Size()
size := v.typecode.Size()
fieldType := structField.Type
fieldSize := fieldType.Size()
if v.isIndirect() || fieldSize > unsafe.Sizeof(uintptr(0)) {
// v.value was already a pointer to the value and it should stay that
// way.
return Value{
flags: flags,
typecode: structField.Type,
typecode: fieldType,
value: unsafe.Pointer(uintptr(v.value) + structField.Offset),
}
}
@ -407,7 +414,7 @@ func (v Value) Field(i int) Value {
// situation explicitly.
return Value{
flags: flags,
typecode: structField.Type,
typecode: fieldType,
value: unsafe.Pointer(uintptr(0)),
}
}
@ -420,7 +427,7 @@ func (v Value) Field(i int) Value {
value := unsafe.Pointer(loadValue(ptr, fieldSize))
return Value{
flags: 0,
typecode: structField.Type,
typecode: fieldType,
value: value,
}
}
@ -430,7 +437,7 @@ func (v Value) Field(i int) Value {
value := maskAndShift(uintptr(v.value), structField.Offset, fieldSize)
return Value{
flags: flags,
typecode: structField.Type,
typecode: fieldType,
value: unsafe.Pointer(value),
}
}
@ -444,10 +451,10 @@ func (v Value) Index(i int) Value {
panic("reflect: slice index out of range")
}
elem := Value{
typecode: v.Type().Elem(),
typecode: v.typecode.elem(),
flags: v.flags | valueFlagIndirect,
}
addr := uintptr(slice.Data) + elem.Type().Size()*uintptr(i) // pointer to new value
addr := uintptr(slice.Data) + elem.typecode.Size()*uintptr(i) // pointer to new value
elem.value = unsafe.Pointer(addr)
return elem
case String:
@ -464,13 +471,13 @@ func (v Value) Index(i int) Value {
}
case Array:
// Extract an element from the array.
elemType := v.Type().Elem()
elemType := v.typecode.elem()
elemSize := elemType.Size()
size := v.Type().Size()
size := v.typecode.Size()
if size == 0 {
// The element size is 0 and/or the length of the array is 0.
return Value{
typecode: v.Type().Elem(),
typecode: v.typecode.elem(),
flags: v.flags,
}
}
@ -481,7 +488,7 @@ func (v Value) Index(i int) Value {
// elemSize.
addr := uintptr(v.value) + elemSize*uintptr(i) // pointer to new value
return Value{
typecode: v.Type().Elem(),
typecode: v.typecode.elem(),
flags: v.flags,
value: unsafe.Pointer(addr),
}
@ -492,7 +499,7 @@ func (v Value) Index(i int) Value {
// Load the value from the pointer.
addr := uintptr(v.value) + elemSize*uintptr(i) // pointer to new value
return Value{
typecode: v.Type().Elem(),
typecode: v.typecode.elem(),
flags: v.flags,
value: unsafe.Pointer(loadValue(unsafe.Pointer(addr), elemSize)),
}
@ -503,7 +510,7 @@ func (v Value) Index(i int) Value {
offset := elemSize * uintptr(i)
value := maskAndShift(uintptr(v.value), offset, elemSize)
return Value{
typecode: v.Type().Elem(),
typecode: v.typecode.elem(),
flags: v.flags,
value: unsafe.Pointer(value),
}
@ -561,10 +568,10 @@ func (it *MapIter) Next() bool {
func (v Value) Set(x Value) {
v.checkAddressable()
if !v.Type().AssignableTo(x.Type()) {
if !v.typecode.AssignableTo(x.typecode) {
panic("reflect: cannot set")
}
size := v.Type().Size()
size := v.typecode.Size()
xptr := x.value
if size <= unsafe.Sizeof(uintptr(0)) && !x.isIndirect() {
value := x.value

14
src/runtime/hashmap.go

@ -351,36 +351,36 @@ func hashmapStringDelete(m *hashmap, key string) {
func hashmapInterfaceHash(itf interface{}) uint32 {
x := reflect.ValueOf(itf)
if x.Type() == 0 {
if x.RawType() == 0 {
return 0 // nil interface
}
value := (*_interface)(unsafe.Pointer(&itf)).value
ptr := value
if x.Type().Size() <= unsafe.Sizeof(uintptr(0)) {
if x.RawType().Size() <= unsafe.Sizeof(uintptr(0)) {
// Value fits in pointer, so it's directly stored in the pointer.
ptr = unsafe.Pointer(&value)
}
switch x.Type().Kind() {
switch x.RawType().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return hashmapHash(ptr, x.Type().Size())
return hashmapHash(ptr, x.RawType().Size())
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return hashmapHash(ptr, x.Type().Size())
return hashmapHash(ptr, x.RawType().Size())
case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
// It should be possible to just has the contents. However, NaN != NaN
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
// time may become exponential. To fix that, it would be better to
// return a random number instead:
// https://research.swtch.com/randhash
return hashmapHash(ptr, x.Type().Size())
return hashmapHash(ptr, x.RawType().Size())
case reflect.String:
return hashmapStringHash(x.String())
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
// It might seem better to just return the pointer, but that won't
// result in an evenly distributed hashmap. Instead, hash the pointer
// like most other types.
return hashmapHash(ptr, x.Type().Size())
return hashmapHash(ptr, x.RawType().Size())
case reflect.Array:
var hash uint32
for i := 0; i < x.Len(); i++ {

11
src/runtime/interface.go

@ -31,18 +31,21 @@ func interfaceEqual(x, y interface{}) bool {
}
func reflectValueEqual(x, y reflect.Value) bool {
if x.Type() == 0 || y.Type() == 0 {
// Note: doing a x.Type() == y.Type() comparison would not work here as that
// would introduce an infinite recursion: comparing two reflect.Type values
// is done with this reflectValueEqual runtime call.
if x.RawType() == 0 || y.RawType() == 0 {
// One of them is nil.
return x.Type() == y.Type()
return x.RawType() == y.RawType()
}
if x.Type() != y.Type() {
if x.RawType() != y.RawType() {
// The type is not the same, which means the interfaces are definitely
// not the same.
return false
}
switch x.Type().Kind() {
switch x.RawType().Kind() {
case reflect.Bool:
return x.Bool() == y.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

Loading…
Cancel
Save