From 7654d86d2ce00494acd0c86e92a90dfcb9ab6bdb Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 28 Feb 2023 16:09:00 -0800 Subject: [PATCH] compiler, reflect: add support for named types --- compiler/interface.go | 9 +++++++-- compiler/testdata/interface.ll | 2 +- src/reflect/type.go | 27 +++++++++++++++++++++++++-- src/reflect/value_test.go | 10 ++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/compiler/interface.go b/compiler/interface.go index 51101065..ca9fc9ba 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -112,6 +112,8 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { typeFieldTypes = append(typeFieldTypes, types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]), + types.NewVar(token.NoPos, nil, "len", types.Typ[types.Uintptr]), + types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(typ.Obj().Name())))), ) case *types.Chan, *types.Slice: typeFieldTypes = append(typeFieldTypes, @@ -169,9 +171,12 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { case *types.Basic: typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))} case *types.Named: + name := typ.Obj().Name() typeFields = []llvm.Value{ - c.getTypeCode(types.NewPointer(typ)), // ptrTo - c.getTypeCode(typ.Underlying()), // underlying + c.getTypeCode(types.NewPointer(typ)), // ptrTo + c.getTypeCode(typ.Underlying()), // underlying + llvm.ConstInt(c.uintptrType, uint64(len(name)), false), // length + c.ctx.ConstString(name, false), // name } metabyte |= 1 << 5 // "named" flag case *types.Chan: diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index 55f189cd..7b44122f 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -9,7 +9,7 @@ target triple = "wasm32-unknown-wasi" @"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 2, ptr @"reflect/types.type:pointer:basic:int" }, align 4 @"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:basic:int" }, align 4 @"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:named:error" }, align 4 -@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 +@"reflect/types.type:named:error" = linkonce_odr constant { i8, ptr, ptr, i32, [5 x i8] } { i8 52, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 5, [5 x i8] c"error" }, align 4 @"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4 @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4 diff --git a/src/reflect/type.go b/src/reflect/type.go index a00af54a..13195b73 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -46,6 +46,12 @@ // - signature types (this is missing input and output parameters): // meta uint8 // ptrTo *typeStruct +// - named types +// meta uint8 +// ptrTo *typeStruct +// elem *typeStruct // underlying type +// nlem uintptr // length of name +// name [1]byte // actual name; length nlem // // The type struct is essentially a union of all the above types. Which it is, // can be determined by looking at the meta byte. @@ -417,6 +423,14 @@ type mapType struct { key *rawType } +type namedType struct { + rawType + ptrTo *rawType + elem *rawType + nlen uintptr + name [1]byte +} + // Type for struct types. The numField value is intentionally put before ptrTo // for better struct packing on 32-bit and 64-bit architectures. On these // architectures, the ptrTo field still has the same offset as in all the other @@ -439,12 +453,16 @@ type structField struct { // Equivalent to (go/types.Type).Underlying(): if this is a named type return // the underlying type, else just return the type itself. func (t *rawType) underlying() *rawType { - if t.meta&flagNamed != 0 { + if t.isNamed() { return (*elemType)(unsafe.Pointer(t)).elem } return t } +func (t *rawType) isNamed() bool { + return t.meta&flagNamed != 0 +} + func TypeOf(i interface{}) Type { return ValueOf(i).typecode } @@ -842,7 +860,12 @@ func (t *rawType) NumMethod() int { } func (t *rawType) Name() string { - panic("unimplemented: (reflect.Type).Name()") + if t.isNamed() { + ntype := (*namedType)(unsafe.Pointer(t)) + return unsafe.String(&ntype.name[0], ntype.nlen) + } + + return t.Kind().String() } func (t *rawType) Key() Type { diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index 53dbc9cb..229fc235 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -213,6 +213,16 @@ func TestBytes(t *testing.T) { } } +func TestNamedTypes(t *testing.T) { + type namedString string + + named := namedString("foo") + if got, want := TypeOf(named).Name(), "namedString"; got != want { + t.Errorf("TypeOf.Name()=%v, want %v", got, want) + } + +} + func equal[T comparable](a, b []T) bool { if len(a) != len(b) { return false