diff --git a/interp/interpreter.go b/interp/interpreter.go index 5d1f024f..21590b6e 100644 --- a/interp/interpreter.go +++ b/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:]) } diff --git a/src/reflect/swapper.go b/src/reflect/swapper.go index 6b8b26a3..f3baaede 100644 --- a/src/reflect/swapper.go +++ b/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) diff --git a/src/reflect/type.go b/src/reflect/type.go index 453fa0d1..61776150 100644 --- a/src/reflect/type.go +++ b/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 diff --git a/src/reflect/value.go b/src/reflect/value.go index 087ddfa1..2bc797e3 100644 --- a/src/reflect/value.go +++ b/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 diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 136fb36b..478014bd 100644 --- a/src/runtime/hashmap.go +++ b/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++ { diff --git a/src/runtime/interface.go b/src/runtime/interface.go index c39d7769..65088363 100644 --- a/src/runtime/interface.go +++ b/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: