Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to implement reflect.New() #1087

Closed
lei-april opened this issue May 8, 2020 · 4 comments
Closed

Trying to implement reflect.New() #1087

lei-april opened this issue May 8, 2020 · 4 comments
Labels
enhancement New feature or request

Comments

@lei-april
Copy link

Hi,

I'm trying to implement the missing function reflect.New() in TinyGo, and here's my minimal patch:

diff --git a/src/reflect/value.go b/src/reflect/value.go
index a1c6937..6e953da 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -650,8 +650,16 @@ func Zero(typ Type) Value {
 	panic("unimplemented: reflect.Zero()")
 }
 
+//go:linkname alloc runtime.alloc
+func alloc(size uintptr) unsafe.Pointer
+
+func PtrTo(t Type) Type {
+	return (t << 5) + Type((Ptr-19)<<1) + 1
+}
+
 func New(typ Type) Value {
-	panic("unimplemented: reflect.New()")
+	data := alloc(typ.Size())
+	return Value{PtrTo(typ), data, 0}
 }
 
 type funcHeader struct {

When testing it with the following sample code:

package main

import "reflect"

type FooInterface interface {
	Hello()
}

type Foo struct {
}

func (f *Foo) Hello() {
	println("Hello")
}

func main() {
	foo := reflect.New(reflect.TypeOf(Foo{})).Interface().(FooInterface)
	foo.Hello()
}

I got the error: panic: runtime error: type assert failed

However, if I change the sample code a little bit, it then works fine:

func touch(i interface{}) {
}

func main() {
	touch(&Foo{}) // ???
	foo := reflect.New(reflect.TypeOf(Foo{})).Interface().(FooInterface)
	foo.Hello()
}

It seems that somehow I need to make the compiler see a conversion from *Foo to interface{}.

Do you have any insights on this? Thanks.

@niaow
Copy link
Member

niaow commented May 8, 2020

Quick explanation of what is happening:

Type asserts are implemented by generating a switch statement. TinyGo tries to compute a list of all implementing types, and then generates a type switch across them.

The way that TinyGo gets this list of all implementing types is by looking at every interface creation operation. In this case, you are synthesizing a type at runtime, and therefore the compiler does not discover the implementation.
So it looks like the compiler will also need to add cases for:

  1. a pointer to a type which has been converted to an interface
  2. all fields of types which have been converted to an interface

Other synthesized types cannot implement non-empty interfaces, so we do not need any gigantic changes to how we handle this yet.

@deadprogram
Copy link
Member

Will we be able to get back to this PR soon?

@dgryski
Copy link
Member

dgryski commented Dec 6, 2021

reflect.New has been implemented and the above tests case works.

~/go/src/github.com/dgryski/bug $ cat bug.go
package main

import "reflect"

type FooInterface interface {
	Hello()
}

type Foo struct {
}

func (f *Foo) Hello() {
	println("Hello")
}

func main() {
	foo := reflect.New(reflect.TypeOf(Foo{})).Interface().(FooInterface)
	foo.Hello()
}
~/go/src/github.com/dgryski/bug $ ~/go/src/github.com/tinygo-org/tinygo/build/tinygo run bug.go
Hello

@deadprogram
Copy link
Member

In that case, I am going to close this issue now. Thanks everyone!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants