go: reflect: StructOf doesn't generate wrapper methods for embedded fields

% go version
go version devel +bbd1dcd Wed Jun 1 21:32:46 2016 +0000 linux/amd64
% cat ~/a.go
package main

import (
        "bytes"
        "io"
        "reflect"
        "sync"
)

func main() {
        t := reflect.StructOf([]reflect.StructField{
                {Type: reflect.TypeOf(sync.Mutex{}), Anonymous: true},
                {Type: reflect.TypeOf(bytes.Buffer{}), Anonymous: true},
        })
        x := reflect.New(t).Interface()
        _ = x.(io.Writer)
}

% go run ~/a.go
panic: interface conversion: *truct { sync.Mutex; bytes.Buffer } is not io.Writer: missing method Write

I expected that x would have generated wrapper methods for Mutex.Lock, etc.

Interestingly, adding this declaration inside func main (not at package level):

var _ interface{} = new(struct {
      sync.Mutex
      bytes.Buffer
})

causes the program to succeed. Presumably it causes the compiler to generate the correct type information, which reflect.StructOf then finds.

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Reactions: 3
  • Comments: 16 (8 by maintainers)

Commits related to this issue

Most upvoted comments

while implementing reflect.NamedOf, I noticed that reflect.StructOf creates wrapper methods for embedded interfaces, and I was surprised that declaring methods at runtime would simply require calling reflect.MakeFunc (see https://github.com/golang/go/blob/master/src/reflect/type.go#L2386).

Alas, it seems not.

The following example crashes with SIGSEGV on both my local Go 1.11.4 installation and on playground https://play.golang.org/p/jny1YKZsHlp

package main

import (
	"fmt"
	"reflect"
)

type MyInt int

func (n MyInt) String() string {
	return fmt.Sprint("MyInt(value = ", int(n), ")")
}

func main() {
	embed()
	embed_reflect()
}

func embed() {
	type S = struct {
		fmt.Stringer
	}
	s := S{
		MyInt(3),
	}
	fmt.Println(s.Stringer.String()) // works, prints 3

	var i fmt.Stringer = s
	fmt.Println(i.String()) // works, prints 3
}

func embed_reflect() {
	tstringer := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()

	t := reflect.StructOf(
		[]reflect.StructField{
			reflect.StructField{
				Name:      tstringer.Name(),
				Type:      tstringer,
				Anonymous: true,
			},
		},
	)
	v := reflect.New(t).Elem()
	v.Field(0).Set(reflect.ValueOf(MyInt(4)))

        i := v.Field(0).Interface().(fmt.Stringer)
	fmt.Println(i.String()) // works, prints 4

	i = v.Interface().(fmt.Stringer)
	fmt.Println(i.String()) // crashes
}

on my local Go 1.11.4, the output is:

MyInt(value = 3)
MyInt(value = 3)
MyInt(value = 4)
unexpected fault address 0xc00000a080
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x2 addr=0xc00000a080 pc=0xc00000a080]

goroutine 1 [running]:
runtime.throw(0x4eebf7, 0x5)
	/usr/local/go/src/runtime/panic.go:608 +0x72 fp=0xc000055e00 sp=0xc000055dd0 pc=0x4288a2
runtime.sigpanic()
	/usr/local/go/src/runtime/signal_unix.go:397 +0x275 fp=0xc000055e50 sp=0xc000055e00 pc=0x43b7f5
main.embed_reflect()
	/home/max/go/src/github.com/cosmos72/gomacro/_example/namedof.go:51 +0x386 fp=0xc000055f88 sp=0xc000055e50 pc=0x4b5486
main.main()
	/home/max/go/src/github.com/cosmos72/gomacro/_example/namedof.go:16 +0x25 fp=0xc000055f98 sp=0xc000055f88 pc=0x4b4f65
runtime.main()
	/usr/local/go/src/runtime/proc.go:201 +0x207 fp=0xc000055fe0 sp=0xc000055f98 pc=0x42a217
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:1333 +0x1 fp=0xc000055fe8 sp=0xc000055fe0 pc=0x452a31

on playground https://play.golang.org/p/jny1YKZsHlp the output is instead:

MyInt(value = 3)
MyInt(value = 3)
MyInt(value = 4)
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0x40c0e0]

goroutine 1 [running]:
main.embed_reflect()
	/tmp/sandbox101416977/main.go:51 +0x400
main.main()
	/tmp/sandbox101416977/main.go:16 +0x40

I would expect that either reflect.StructOf fails (due to unsupported interface embedding) or that its returned type does not implement fmt.Stringer - I was surely not expecting that the method call crashes at runtime.