diff options
author | Ayke van Laethem <[email protected]> | 2021-04-11 13:50:44 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-04-12 14:49:26 +0200 |
commit | e587b1d1b4031a7bed960d94122a5d243229f090 (patch) | |
tree | 3f234181c5578a4a0737b11327178222095bea7c | |
parent | 57271d7eaa50b122706610fc5c406e0380965998 (diff) | |
download | tinygo-e587b1d1b4031a7bed960d94122a5d243229f090.tar.gz tinygo-e587b1d1b4031a7bed960d94122a5d243229f090.zip |
reflect: implement New function
This is very important for some use cases, for example for Vecty.
-rw-r--r-- | compiler/compiler.go | 2 | ||||
-rw-r--r-- | compiler/interface.go | 34 | ||||
-rw-r--r-- | compiler/testdata/interface.ll | 17 | ||||
-rw-r--r-- | src/reflect/type.go | 3 | ||||
-rw-r--r-- | src/reflect/value.go | 11 | ||||
-rw-r--r-- | src/runtime/interface.go | 5 | ||||
-rw-r--r-- | testdata/reflect.go | 15 | ||||
-rw-r--r-- | testdata/reflect.txt | 25 |
8 files changed, 87 insertions, 25 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index 0afa20daf..c6e577fd6 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 8 // last change: don't use runtime.typecodeID in func lowering +const Version = 9 // last change: implement reflect.New() func init() { llvm.InitializeAllTargets() diff --git a/compiler/interface.go b/compiler/interface.go index 8b3988a12..eda7e3653 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -46,6 +46,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { var references llvm.Value var length int64 var methodSet llvm.Value + var ptrTo llvm.Value switch typ := typ.(type) { case *types.Named: references = c.getTypeCode(typ.Underlying()) @@ -69,22 +70,25 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { if _, ok := typ.Underlying().(*types.Interface); !ok { methodSet = c.getTypeMethodSet(typ) } - if !references.IsNil() || length != 0 || !methodSet.IsNil() { - // Set the 'references' field of the runtime.typecodeID struct. - globalValue := llvm.ConstNull(global.Type().ElementType()) - if !references.IsNil() { - globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0}) - } - if length != 0 { - lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false) - globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1}) - } - if !methodSet.IsNil() { - globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2}) - } - global.SetInitializer(globalValue) - global.SetLinkage(llvm.LinkOnceODRLinkage) + if _, ok := typ.Underlying().(*types.Pointer); !ok { + ptrTo = c.getTypeCode(types.NewPointer(typ)) + } + globalValue := llvm.ConstNull(global.Type().ElementType()) + if !references.IsNil() { + globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0}) + } + if length != 0 { + lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false) + globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1}) + } + if !methodSet.IsNil() { + globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2}) + } + if !ptrTo.IsNil() { + globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3}) } + global.SetInitializer(globalValue) + global.SetLinkage(llvm.LinkOnceODRLinkage) global.SetGlobalConstant(true) } return global diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index e5aa7eef1..1414db8c3 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -3,20 +3,21 @@ source_filename = "interface.go" target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128" target triple = "i686--linux" -%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* } +%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* } %runtime.interfaceMethodInfo = type { i8*, i32 } %runtime._interface = type { i32, i8* } %runtime._string = type { i8*, i32 } -@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer -@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null } -@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null } -@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null } -@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null } +@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int" } +@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } +@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } +@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error" } +@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" } @"func Error() string" = external constant i8 @"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] -@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null } -@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null } +@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } +@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null } +@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" } @"func String() string" = external constant i8 @"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"] @"reflect/types.typeid:basic:int" = external constant i8 diff --git a/src/reflect/type.go b/src/reflect/type.go index 67396c777..2e7096759 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -322,6 +322,9 @@ func TypeOf(i interface{}) Type { } func PtrTo(t Type) Type { + if t.Kind() == Ptr { + panic("reflect: cannot make **T type") + } ptrType := t.(rawType)<<5 | 5 // 0b0101 == 5 if ptrType>>5 != t { panic("reflect: PtrTo type does not fit") diff --git a/src/reflect/value.go b/src/reflect/value.go index 28308f751..08d828d80 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -724,8 +724,14 @@ func Zero(typ Type) Value { panic("unimplemented: reflect.Zero()") } +// New is the reflect equivalent of the new(T) keyword, returning a pointer to a +// new value of the given type. func New(typ Type) Value { - panic("unimplemented: reflect.New()") + return Value{ + typecode: PtrTo(typ).(rawType), + value: alloc(typ.Size()), + flags: valueFlagExported, + } } type funcHeader struct { @@ -756,6 +762,9 @@ func (e *ValueError) Error() string { // llvm.memcpy.p0i8.p0i8.i32(). func memcpy(dst, src unsafe.Pointer, size uintptr) +//go:linkname alloc runtime.alloc +func alloc(size uintptr) unsafe.Pointer + // Copy copies the contents of src into dst until either // dst has been filled or src has been exhausted. func Copy(dst, src Value) int { diff --git a/src/runtime/interface.go b/src/runtime/interface.go index 650883634..10faa4eca 100644 --- a/src/runtime/interface.go +++ b/src/runtime/interface.go @@ -112,6 +112,11 @@ type typecodeID struct { length uintptr methodSet *interfaceMethodInfo // nil or a GEP of an array + + // The type that's a pointer to this type, nil if it is already a pointer. + // Keeping the type struct alive here is important so that values from + // reflect.New (which uses reflect.PtrTo) can be used in type asserts etc. + ptrTo *typecodeID } // structField is used by the compiler to pass information to the interface diff --git a/testdata/reflect.go b/testdata/reflect.go index b02dd6c80..cd71d984a 100644 --- a/testdata/reflect.go +++ b/testdata/reflect.go @@ -124,6 +124,21 @@ func main() { showValue(reflect.ValueOf(v), "") } + // Test reflect.New(). + newInt8 := reflect.New(reflect.TypeOf(int8(0))) + newInt8.Elem().SetInt(5) + newInt16 := reflect.New(reflect.TypeOf(int16(0))) + newInt16.Elem().SetInt(-800) + newInt32 := reflect.New(reflect.TypeOf(int32(0))) + newInt32.Elem().SetInt(1e8) + newInt64 := reflect.New(reflect.TypeOf(int64(0))) + newInt64.Elem().SetInt(-1e12) + newComplex128 := reflect.New(reflect.TypeOf(0 + 0i)) + newComplex128.Elem().SetComplex(-8 - 20e5i) + for _, val := range []reflect.Value{newInt8, newInt16, newInt32, newInt64, newComplex128} { + showValue(val, "") + } + // test sizes println("\nsizes:") for _, tc := range []struct { diff --git a/testdata/reflect.txt b/testdata/reflect.txt index af9e7231f..4f04afe98 100644 --- a/testdata/reflect.txt +++ b/testdata/reflect.txt @@ -321,6 +321,31 @@ reflect type: ptr embedded: false reflect type: int addrable=true int: 42 +reflect type: ptr + pointer: true int8 + nil: false + reflect type: int8 settable=true addrable=true + int: 5 +reflect type: ptr + pointer: true int16 + nil: false + reflect type: int16 settable=true addrable=true + int: -800 +reflect type: ptr + pointer: true int32 + nil: false + reflect type: int32 settable=true addrable=true + int: 100000000 +reflect type: ptr + pointer: true int64 + nil: false + reflect type: int64 settable=true addrable=true + int: -1000000000000 +reflect type: ptr + pointer: true complex128 + nil: false + reflect type: complex128 settable=true addrable=true + complex: (-8.000000e+000-2.000000e+006i) sizes: int8 1 8 |