reflect: add limited support for all type kinds

This commit makes sure all Go types can be encoded in the interface type
code, so that Type.Kind() always returns a proper type kind for any
non-nil interface.
Этот коммит содержится в:
Ayke van Laethem 2019-01-20 17:30:27 +01:00
родитель 101f2e519b
коммит dfef168139
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
6 изменённых файлов: 377 добавлений и 66 удалений

Просмотреть файл

@ -8,6 +8,8 @@ package compiler
import ( import (
"go/token" "go/token"
"go/types" "go/types"
"strconv"
"strings"
"github.com/tinygo-org/tinygo/ir" "github.com/tinygo-org/tinygo/ir"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
@ -92,55 +94,91 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
// interface lowering pass to assign type codes as expected by the reflect // interface lowering pass to assign type codes as expected by the reflect
// package. See getTypeCodeNum. // package. See getTypeCodeNum.
func getTypeCodeName(t types.Type) string { func getTypeCodeName(t types.Type) string {
name := ""
if named, ok := t.(*types.Named); ok {
name = "~" + named.String() + ":"
t = t.Underlying()
}
switch t := t.(type) { switch t := t.(type) {
case *types.Array:
return "array:" + name + strconv.FormatInt(t.Len(), 10) + ":" + getTypeCodeName(t.Elem())
case *types.Basic: case *types.Basic:
var name string var kind string
switch t.Kind() { switch t.Kind() {
case types.Bool: case types.Bool:
name = "bool" kind = "bool"
case types.Int: case types.Int:
name = "int" kind = "int"
case types.Int8: case types.Int8:
name = "int8" kind = "int8"
case types.Int16: case types.Int16:
name = "int16" kind = "int16"
case types.Int32: case types.Int32:
name = "int32" kind = "int32"
case types.Int64: case types.Int64:
name = "int64" kind = "int64"
case types.Uint: case types.Uint:
name = "uint" kind = "uint"
case types.Uint8: case types.Uint8:
name = "uint8" kind = "uint8"
case types.Uint16: case types.Uint16:
name = "uint16" kind = "uint16"
case types.Uint32: case types.Uint32:
name = "uint32" kind = "uint32"
case types.Uint64: case types.Uint64:
name = "uint64" kind = "uint64"
case types.Uintptr: case types.Uintptr:
name = "uintptr" kind = "uintptr"
case types.Float32: case types.Float32:
name = "float32" kind = "float32"
case types.Float64: case types.Float64:
name = "float64" kind = "float64"
case types.Complex64: case types.Complex64:
name = "complex64" kind = "complex64"
case types.Complex128: case types.Complex128:
name = "complex128" kind = "complex128"
case types.String: case types.String:
name = "string" kind = "string"
case types.UnsafePointer: case types.UnsafePointer:
name = "unsafeptr" kind = "unsafeptr"
default: default:
panic("unknown basic type: " + t.Name()) panic("unknown basic type: " + t.Name())
} }
return "basic:" + name return "basic:" + name + kind
case *types.Chan:
return "chan:" + name + getTypeCodeName(t.Elem())
case *types.Interface:
methods := make([]string, t.NumMethods())
for i := 0; i < t.NumMethods(); i++ {
methods[i] = getTypeCodeName(t.Method(i).Type())
}
return "interface:" + name + "{" + strings.Join(methods, ",") + "}"
case *types.Map:
keyType := getTypeCodeName(t.Key())
elemType := getTypeCodeName(t.Elem())
return "map:" + name + "{" + keyType + "," + elemType + "}"
case *types.Pointer:
return "pointer:" + name + getTypeCodeName(t.Elem())
case *types.Signature:
params := make([]string, t.Params().Len())
for i := 0; i < t.Params().Len(); i++ {
params[i] = getTypeCodeName(t.Params().At(i).Type())
}
results := make([]string, t.Results().Len())
for i := 0; i < t.Results().Len(); i++ {
results[i] = getTypeCodeName(t.Results().At(i).Type())
}
return "func:" + name + "{" + strings.Join(params, ",") + "}{" + strings.Join(results, ",") + "}"
case *types.Slice: case *types.Slice:
return "slice:" + getTypeCodeName(t.Elem()) return "slice:" + name + getTypeCodeName(t.Elem())
case *types.Struct:
elems := make([]string, t.NumFields())
for i := 0; i < t.NumFields(); i++ {
elems[i] = getTypeCodeName(t.Field(i).Type())
}
return "struct:" + name + "{" + strings.Join(elems, ",") + "}"
default: default:
// Unknown type, fall back to the .String() method for identification. panic("unknown type: " + t.String())
return "other:" + t.String()
} }
} }

Просмотреть файл

@ -39,18 +39,12 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
// Assign typecodes the way the reflect package expects. // Assign typecodes the way the reflect package expects.
fallbackIndex := 1 fallbackIndex := 1
namedTypes := make(map[string]int)
for _, t := range typeSlice { for _, t := range typeSlice {
if t.name[:5] != "type:" { if t.name[:5] != "type:" {
panic("expected type name to start with 'type:'") panic("expected type name to start with 'type:'")
} }
num := c.getTypeCodeNum(t.name[5:]) num := c.getTypeCodeNum(t.name[5:], &fallbackIndex, namedTypes)
if num == nil {
// Fallback/unsupported types have a typecode with the lowest bits
// set to 11.
t.num = uint64(fallbackIndex<<2 | 3)
fallbackIndex++
continue
}
if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() { if num.BitLen() > c.uintptrType.IntTypeWidth() || !num.IsUint64() {
// TODO: support this in some way, using a side table for example. // TODO: support this in some way, using a side table for example.
// That's less efficient but better than not working at all. // That's less efficient but better than not working at all.
@ -65,20 +59,98 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
// getTypeCodeNum returns the typecode for a given type as expected by the // getTypeCodeNum returns the typecode for a given type as expected by the
// reflect package. Also see getTypeCodeName, which serializes types to a string // reflect package. Also see getTypeCodeName, which serializes types to a string
// based on a types.Type value for this function. // based on a types.Type value for this function.
func (c *Compiler) getTypeCodeNum(name string) *big.Int { func (c *Compiler) getTypeCodeNum(id string, fallbackIndex *int, namedTypes map[string]int) *big.Int {
if strings.HasPrefix(name, "basic:") { // Note: see src/reflect/type.go for bit allocations.
// Basic types have a typecode with the lowest bits set to 00. // A type can be named or unnamed. Example of both:
num, ok := basicTypes[name[len("basic:"):]] // basic:~foo:uint64
// basic:uint64
// Extract the class (basic, slice, pointer, etc.), the name, and the
// contents of this type ID string. Allocate bits based on that, as
// src/runtime/types.go expects.
class := id[:strings.IndexByte(id, ':')]
value := id[len(class)+1:]
name := ""
if value[0] == '~' {
name = value[1:strings.IndexByte(value, ':')]
value = value[len(name)+2:]
}
if class == "basic" {
// Basic types follow the following bit pattern:
// ...xxxxx0
// where xxxxx is allocated for the 18 possible basic types and all the
// upper bits are used to indicate the named type.
num, ok := basicTypes[value]
if !ok { if !ok {
panic("invalid basic type: " + name) panic("invalid basic type: " + id)
} }
return big.NewInt(num<<2 | 0) if name != "" {
} else if strings.HasPrefix(name, "slice:") { // This type is named, set the upper bits to the name ID.
// Slices have a typecode with the lowest bits set to 01. num |= int64(getNamedTypeNum(namedTypes, name)) << 5
num := c.getTypeCodeNum(name[len("slice:"):]) }
num.Lsh(num, 2).Or(num, big.NewInt(1)) return big.NewInt(num << 1)
return num
} else { } else {
return nil // Complex types use the following bit pattern:
// ...nxxx1
// where xxx indicates the complex type (any non-basic type). The upper
// bits contain whatever the type contains. Types that wrap a single
// other type (channel, interface, pointer, slice) just contain the bits
// of the wrapped type. Other types (like struct) have a different
// method of encoding the contents of the type.
var num *big.Int
var classNumber int64
switch class {
case "chan":
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
classNumber = 0
case "interface":
num = big.NewInt(int64(*fallbackIndex))
*fallbackIndex++
classNumber = 1
case "pointer":
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
classNumber = 2
case "slice":
num = c.getTypeCodeNum(value, fallbackIndex, namedTypes)
classNumber = 3
case "array":
num = big.NewInt(int64(*fallbackIndex))
*fallbackIndex++
classNumber = 4
case "func":
num = big.NewInt(int64(*fallbackIndex))
*fallbackIndex++
classNumber = 5
case "map":
num = big.NewInt(int64(*fallbackIndex))
*fallbackIndex++
classNumber = 6
case "struct":
num = big.NewInt(int64(*fallbackIndex))
*fallbackIndex++
classNumber = 7
default:
panic("unknown type kind: " + id)
}
if name == "" {
num.Lsh(num, 5).Or(num, big.NewInt((classNumber<<1)+1))
} else {
// TODO: store num in a sidetable
num = big.NewInt(int64(getNamedTypeNum(namedTypes, name))<<1 | 1)
num.Lsh(num, 4).Or(num, big.NewInt((classNumber<<1)+1))
}
return num
}
}
// getNamedTypeNum returns an appropriate (unique) number for the given named
// type. If the name already has a number that number is returned, else a new
// number is returned. The number is always non-zero.
func getNamedTypeNum(namedTypes map[string]int, name string) int {
if num, ok := namedTypes[name]; ok {
return num
} else {
num = len(namedTypes) + 1
namedTypes[name] = num
return num
} }
} }

Просмотреть файл

@ -4,10 +4,26 @@ import (
"unsafe" "unsafe"
) )
// A Kind is the number that the compiler uses for this type. // The compiler uses a compact encoding to store type information. Unlike the
// main Go compiler, most of the types are stored directly in the type code.
// //
// Not used directly. These types are all replaced with the number the compiler // Type code bit allocation:
// uses internally for the type. // xxxxx0: basic types, where xxxxx is the basic type number (never 0).
// The higher bits indicate the named type, if any.
// nxxx1: complex types, where n indicates whether this is a named type (named
// if set) and xxx contains the type kind number:
// 0 (0001): Chan
// 1 (0011): Interface
// 2 (0101): Ptr
// 3 (0111): Slice
// 4 (1001): Array
// 5 (1011): Func
// 6 (1101): Map
// 7 (1111): Struct
// The higher bits are either the contents of the type depending on the
// type (if n is clear) or indicate the number of the named type (if n
// is set).
type Kind uintptr type Kind uintptr
// Copied from reflect/type.go // Copied from reflect/type.go
@ -32,13 +48,13 @@ const (
Complex128 Complex128
String String
UnsafePointer UnsafePointer
Array
Chan Chan
Func
Interface Interface
Map
Ptr Ptr
Slice Slice
Array
Func
Map
Struct Struct
) )
@ -80,8 +96,22 @@ func (k Kind) String() string {
return "string" return "string"
case UnsafePointer: case UnsafePointer:
return "unsafe.Pointer" return "unsafe.Pointer"
case Chan:
return "chan"
case Interface:
return "interface"
case Ptr:
return "ptr"
case Slice: case Slice:
return "slice" return "slice"
case Array:
return "array"
case Func:
return "func"
case Map:
return "map"
case Struct:
return "struct"
default: default:
return "invalid" return "invalid"
} }
@ -89,7 +119,7 @@ func (k Kind) String() string {
// basicType returns a new Type for this kind if Kind is a basic type. // basicType returns a new Type for this kind if Kind is a basic type.
func (k Kind) basicType() Type { func (k Kind) basicType() Type {
return Type(k << 2 | 0) return Type(k << 1)
} }
// The typecode as used in an interface{}. // The typecode as used in an interface{}.
@ -104,22 +134,22 @@ func (t Type) String() string {
} }
func (t Type) Kind() Kind { func (t Type) Kind() Kind {
if t % 4 == 0 { if t % 2 == 0 {
// Basic type // basic type
return Kind(t >> 2) return Kind((t >> 1) % 32)
} else if t % 4 == 1 {
// Slice
return Slice
} else { } else {
return Invalid // TODO return Kind(t >> 1) % 8 + 19
} }
} }
func (t Type) Elem() Type { func (t Type) Elem() Type {
switch t.Kind() { switch t.Kind() {
case Slice: case Chan, Ptr, Slice:
return t >> 2 if (t >> 4) % 2 != 0 {
default: // not implemented: Array, Chan, Map, Ptr panic("unimplemented: (reflect.Type).Elem() for named types")
}
return t >> 5
default: // not implemented: Array, Map
panic("unimplemented: (reflect.Type).Elem()") panic("unimplemented: (reflect.Type).Elem()")
} }
} }
@ -164,7 +194,7 @@ func (t Type) Size() uintptr {
return 16 return 16
case String: case String:
return unsafe.Sizeof(StringHeader{}) return unsafe.Sizeof(StringHeader{})
case UnsafePointer: case UnsafePointer, Chan, Map, Ptr:
return unsafe.Sizeof(uintptr(0)) return unsafe.Sizeof(uintptr(0))
case Slice: case Slice:
return unsafe.Sizeof(SliceHeader{}) return unsafe.Sizeof(SliceHeader{})

Просмотреть файл

@ -31,16 +31,38 @@ func (v Value) Kind() Kind {
} }
func (v Value) IsNil() bool { func (v Value) IsNil() bool {
panic("unimplemented: (reflect.Value).IsNil()") switch v.Kind() {
case Chan, Map, Ptr:
return v.value == nil
case Func:
if v.value == nil {
return true
}
fn := (*funcHeader)(v.value)
return fn.Code == nil
case Slice:
if v.value == nil {
return true
}
slice := (*SliceHeader)(v.value)
return slice.Data == 0
case Interface:
panic("unimplemented: (reflect.Value).IsNil()")
default:
panic(&ValueError{"IsNil"})
}
} }
func (v Value) Pointer() uintptr { func (v Value) Pointer() uintptr {
switch v.Kind() { switch v.Kind() {
case UnsafePointer: case Chan, Map, Ptr, UnsafePointer:
return uintptr(v.value) return uintptr(v.value)
case Chan, Func, Map, Ptr, Slice: case Slice:
slice := (*SliceHeader)(v.value)
return slice.Data
case Func:
panic("unimplemented: (reflect.Value).Pointer()") panic("unimplemented: (reflect.Value).Pointer()")
default: default: // not implemented: Func
panic(&ValueError{"Pointer"}) panic(&ValueError{"Pointer"})
} }
} }
@ -322,6 +344,11 @@ func MakeSlice(typ Type, len, cap int) Value {
panic("unimplemented: reflect.MakeSlice()") panic("unimplemented: reflect.MakeSlice()")
} }
type funcHeader struct {
Context unsafe.Pointer
Code unsafe.Pointer
}
type SliceHeader struct { type SliceHeader struct {
Data uintptr Data uintptr
Len uintptr Len uintptr

81
testdata/reflect.go предоставленный
Просмотреть файл

@ -5,16 +5,29 @@ import (
"unsafe" "unsafe"
) )
type myint int type (
myint int
myslice []byte
myslice2 []myint
)
func main() { func main() {
println("matching types") println("matching types")
println(reflect.TypeOf(int(3)) == reflect.TypeOf(int(5))) println(reflect.TypeOf(int(3)) == reflect.TypeOf(int(5)))
println(reflect.TypeOf(int(3)) == reflect.TypeOf(uint(5))) println(reflect.TypeOf(int(3)) == reflect.TypeOf(uint(5)))
println(reflect.TypeOf(myint(3)) == reflect.TypeOf(int(5))) println(reflect.TypeOf(myint(3)) == reflect.TypeOf(int(5)))
println(reflect.TypeOf(myslice{}) == reflect.TypeOf([]byte{}))
println(reflect.TypeOf(myslice2{}) == reflect.TypeOf([]myint{}))
println(reflect.TypeOf(myslice2{}) == reflect.TypeOf([]int{}))
println("\nvalues of interfaces") println("\nvalues of interfaces")
var zeroSlice []byte
var zeroFunc func()
var zeroMap map[string]int
var zeroChan chan int
n := 42
for _, v := range []interface{}{ for _, v := range []interface{}{
// basic types
true, true,
false, false,
int(2000), int(2000),
@ -37,15 +50,57 @@ func main() {
float64(3.14), float64(3.14),
complex64(1.2 + 0.3i), complex64(1.2 + 0.3i),
complex128(1.3 + 0.4i), complex128(1.3 + 0.4i),
myint(32),
"foo", "foo",
unsafe.Pointer(new(int)), unsafe.Pointer(new(int)),
// channels
zeroChan,
// pointers
new(int),
new(error),
&n,
// slices
[]byte{1, 2, 3}, []byte{1, 2, 3},
make([]uint8, 2, 5), make([]uint8, 2, 5),
[]rune{3, 5}, []rune{3, 5},
[]string{"xyz", "Z"}, []string{"xyz", "Z"},
zeroSlice,
[]byte{},
// array
[4]int{1, 2, 3, 4},
// functions
zeroFunc,
emptyFunc,
// maps
zeroMap,
map[string]int{},
// structs
struct{}{},
struct{ error }{},
} { } {
showValue(v, "") showValue(v, "")
} }
// test sizes
println("\nsizes:")
println("int8", int(reflect.TypeOf(int8(0)).Size()))
println("int16", int(reflect.TypeOf(int16(0)).Size()))
println("int32", int(reflect.TypeOf(int32(0)).Size()))
println("int64", int(reflect.TypeOf(int64(0)).Size()))
println("uint8", int(reflect.TypeOf(uint8(0)).Size()))
println("uint16", int(reflect.TypeOf(uint16(0)).Size()))
println("uint32", int(reflect.TypeOf(uint32(0)).Size()))
println("uint64", int(reflect.TypeOf(uint64(0)).Size()))
println("float32", int(reflect.TypeOf(float32(0)).Size()))
println("float64", int(reflect.TypeOf(float64(0)).Size()))
println("complex64", int(reflect.TypeOf(complex64(0)).Size()))
println("complex128", int(reflect.TypeOf(complex128(0)).Size()))
assertSize(reflect.TypeOf(uintptr(0)).Size() == unsafe.Sizeof(uintptr(0)), "uintptr")
assertSize(reflect.TypeOf("").Size() == unsafe.Sizeof(""), "string")
assertSize(reflect.TypeOf(new(int)).Size() == unsafe.Sizeof(new(int)), "*int")
}
func emptyFunc() {
} }
func showValue(v interface{}, indent string) { func showValue(v interface{}, indent string) {
@ -79,13 +134,37 @@ func showValue(v interface{}, indent string) {
} }
case reflect.UnsafePointer: case reflect.UnsafePointer:
println(indent+" pointer:", rv.Pointer() != 0) println(indent+" pointer:", rv.Pointer() != 0)
case reflect.Array:
println(indent + " array")
case reflect.Chan:
println(indent+" chan:", rt.Elem().Kind().String())
println(indent+" nil:", rv.IsNil())
case reflect.Func:
println(indent + " func")
println(indent+" nil:", rv.IsNil())
case reflect.Map:
println(indent + " map")
println(indent+" nil:", rv.IsNil())
case reflect.Ptr:
println(indent+" pointer:", rv.Pointer() != 0, rt.Elem().Kind().String())
println(indent+" nil:", rv.IsNil())
case reflect.Slice: case reflect.Slice:
println(indent+" slice:", rt.Elem().Kind().String(), rv.Len(), rv.Cap()) println(indent+" slice:", rt.Elem().Kind().String(), rv.Len(), rv.Cap())
println(indent+" pointer:", rv.Pointer() != 0)
println(indent+" nil:", rv.IsNil())
for i := 0; i < rv.Len(); i++ { for i := 0; i < rv.Len(); i++ {
println(indent+" indexing:", i) println(indent+" indexing:", i)
showValue(rv.Index(i).Interface(), indent+" ") showValue(rv.Index(i).Interface(), indent+" ")
} }
case reflect.Struct:
println(indent + " struct")
default: default:
println(indent + " unknown type kind!") println(indent + " unknown type kind!")
} }
} }
func assertSize(ok bool, typ string) {
if !ok {
panic("size mismatch for type " + typ)
}
}

65
testdata/reflect.txt предоставленный
Просмотреть файл

@ -2,6 +2,9 @@ matching types
true true
false false
false false
false
false
false
values of interfaces values of interfaces
reflect type: bool reflect type: bool
@ -48,6 +51,8 @@ reflect type: complex64
complex: (+1.200000e+000+3.000000e-001i) complex: (+1.200000e+000+3.000000e-001i)
reflect type: complex128 reflect type: complex128
complex: (+1.300000e+000+4.000000e-001i) complex: (+1.300000e+000+4.000000e-001i)
reflect type: int
int: 32
reflect type: string reflect type: string
string: foo 3 string: foo 3
reflect type: uint8 reflect type: uint8
@ -58,8 +63,22 @@ reflect type: string
uint: 111 uint: 111
reflect type: unsafe.Pointer reflect type: unsafe.Pointer
pointer: true pointer: true
reflect type: chan
chan: int
nil: true
reflect type: ptr
pointer: true int
nil: false
reflect type: ptr
pointer: true interface
nil: false
reflect type: ptr
pointer: true int
nil: false
reflect type: slice reflect type: slice
slice: uint8 3 3 slice: uint8 3 3
pointer: true
nil: false
indexing: 0 indexing: 0
reflect type: uint8 reflect type: uint8
uint: 1 uint: 1
@ -71,6 +90,8 @@ reflect type: slice
uint: 3 uint: 3
reflect type: slice reflect type: slice
slice: uint8 2 5 slice: uint8 2 5
pointer: true
nil: false
indexing: 0 indexing: 0
reflect type: uint8 reflect type: uint8
uint: 0 uint: 0
@ -79,6 +100,8 @@ reflect type: slice
uint: 0 uint: 0
reflect type: slice reflect type: slice
slice: int32 2 2 slice: int32 2 2
pointer: true
nil: false
indexing: 0 indexing: 0
reflect type: int32 reflect type: int32
int: 3 int: 3
@ -87,6 +110,8 @@ reflect type: slice
int: 5 int: 5
reflect type: slice reflect type: slice
slice: string 2 2 slice: string 2 2
pointer: true
nil: false
indexing: 0 indexing: 0
reflect type: string reflect type: string
string: xyz 3 string: xyz 3
@ -101,3 +126,43 @@ reflect type: slice
string: Z 1 string: Z 1
reflect type: uint8 reflect type: uint8
uint: 90 uint: 90
reflect type: slice
slice: uint8 0 0
pointer: false
nil: true
reflect type: slice
slice: uint8 0 0
pointer: true
nil: false
reflect type: array
array
reflect type: func
func
nil: true
reflect type: func
func
nil: false
reflect type: map
map
nil: true
reflect type: map
map
nil: false
reflect type: struct
struct
reflect type: struct
struct
sizes:
int8 1
int16 2
int32 4
int64 8
uint8 1
uint16 2
uint32 4
uint64 8
float32 4
float64 8
complex64 8
complex128 16