diff --git a/go/ssa/interp/external.go b/go/ssa/interp/external.go index e3a8ee07..fc43366a 100644 --- a/go/ssa/interp/external.go +++ b/go/ssa/interp/external.go @@ -80,6 +80,7 @@ func init() { "math.Min": ext۰math۰Min, "os.runtime_args": ext۰os۰runtime_args, "reflect.New": ext۰reflect۰New, + "reflect.SliceOf": ext۰reflect۰SliceOf, "reflect.TypeOf": ext۰reflect۰TypeOf, "reflect.ValueOf": ext۰reflect۰ValueOf, "reflect.init": ext۰reflect۰Init, diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go index 16a76a86..825d2d0d 100644 --- a/go/ssa/interp/interp.go +++ b/go/ssa/interp/interp.go @@ -631,6 +631,20 @@ func init() { environ = append(environ, "GOARCH="+runtime.GOARCH) } +// deleteBodies delete the bodies of all standalone functions except the +// specified ones. A missing intrinsic leads to a clear runtime error. +func deleteBodies(pkg *ssa.Package, except ...string) { + keep := make(map[string]bool) + for _, e := range except { + keep[e] = true + } + for _, mem := range pkg.Members { + if fn, ok := mem.(*ssa.Function); ok && !keep[fn.Name()] { + fn.Blocks = nil + } + } +} + // Interpret interprets the Go program whose main package is mainpkg. // mode specifies various interpreter options. filename and args are // the initial values of os.Args for the target program. sizes is the @@ -676,22 +690,13 @@ func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename stri case "syscall": setGlobal(i, pkg, "envs", environ) + case "reflect": + deleteBodies(pkg, "DeepEqual", "deepValueEqual") + case "runtime": sz := sizes.Sizeof(pkg.Object.Scope().Lookup("MemStats").Type()) setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz)) - - // Delete the bodies of almost all "runtime" functions since they're magic. - // A missing intrinsic leads to a very clear error. - for _, mem := range pkg.Members { - if fn, ok := mem.(*ssa.Function); ok { - switch fn.Name() { - case "GOROOT", "gogetenv": - // keep - default: - fn.Blocks = nil - } - } - } + deleteBodies(pkg, "GOROOT", "gogetenv") } } @@ -712,7 +717,7 @@ func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename stri case string: fmt.Fprintln(os.Stderr, "panic:", p) default: - fmt.Fprintf(os.Stderr, "panic: unexpected type: %T\n", p) + fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p) } // TODO(adonovan): dump panicking interpreter goroutine? diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go index 456fdaf7..5d458f30 100644 --- a/go/ssa/interp/interp_test.go +++ b/go/ssa/interp/interp_test.go @@ -146,6 +146,7 @@ var testdataTests = []string{ "mrvchain.go", "range.go", "recover.go", + "reflect.go", "static.go", "callstack.go", } @@ -276,11 +277,13 @@ func printFailures(failures []string) { } } -// The "normal" success predicate. -func exitsZero(exitcode int, _ string) error { +func success(exitcode int, output string) error { if exitcode != 0 { return fmt.Errorf("exit code was %d", exitcode) } + if strings.Contains(output, "BUG") { + return fmt.Errorf("exited zero but output contained 'BUG'") + } return nil } @@ -288,7 +291,7 @@ func exitsZero(exitcode int, _ string) error { func TestTestdataFiles(t *testing.T) { var failures []string for _, input := range testdataTests { - if !run(t, "testdata"+slash, input, exitsZero) { + if !run(t, "testdata"+slash, input, success) { failures = append(failures, input) } } @@ -303,16 +306,6 @@ func TestGorootTest(t *testing.T) { var failures []string - // $GOROOT/tests are also considered a failure if they print "BUG". - success := func(exitcode int, output string) error { - if exitcode != 0 { - return fmt.Errorf("exit code was %d", exitcode) - } - if strings.Contains(output, "BUG") { - return fmt.Errorf("exited zero but output contained 'BUG'") - } - return nil - } for _, input := range gorootTestTests { if !run(t, filepath.Join(build.Default.GOROOT, "test")+slash, input, success) { failures = append(failures, input) diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go index 051695cc..fd190df9 100644 --- a/go/ssa/interp/reflect.go +++ b/go/ssa/interp/reflect.go @@ -150,8 +150,13 @@ func ext۰reflect۰New(fr *frame, args []value) value { return makeReflectValue(types.NewPointer(t), &alloc) } +func ext۰reflect۰SliceOf(fr *frame, args []value) value { + // Signature: func (t reflect.rtype) Type + return makeReflectType(rtype{types.NewSlice(args[0].(iface).v.(rtype).t)}) +} + func ext۰reflect۰TypeOf(fr *frame, args []value) value { - // Signature: func (t reflect.rtype) string + // Signature: func (t reflect.rtype) Type return makeReflectType(rtype{args[0].(iface).t}) } diff --git a/go/ssa/interp/testdata/reflect.go b/go/ssa/interp/testdata/reflect.go new file mode 100644 index 00000000..6aa9a67a --- /dev/null +++ b/go/ssa/interp/testdata/reflect.go @@ -0,0 +1,11 @@ +package main + +import "reflect" + +func main() { + // Regression test for issue 9462. + got := reflect.SliceOf(reflect.TypeOf(byte(0))).String() + if got != "[]uint8" && got != "[]byte" { // result varies by toolchain + println("BUG: " + got) + } +}