diff options
Diffstat (limited to 'go/ssa/interp')
-rw-r--r-- | go/ssa/interp/interp.go | 10 | ||||
-rw-r--r-- | go/ssa/interp/interp_test.go | 72 | ||||
-rw-r--r-- | go/ssa/interp/testdata/src/encoding/encoding.go | 15 | ||||
-rw-r--r-- | go/ssa/interp/testdata/src/log/log.go | 8 |
4 files changed, 102 insertions, 3 deletions
diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go index 0f6a21f92..2b21aad70 100644 --- a/go/ssa/interp/interp.go +++ b/go/ssa/interp/interp.go @@ -51,6 +51,7 @@ import ( "os" "reflect" "runtime" + "strings" "sync/atomic" "golang.org/x/tools/go/ssa" @@ -505,7 +506,11 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, return ext(fr, args) } if fn.Blocks == nil { - panic("no code for function: " + name) + var reason string // empty by default + if strings.HasPrefix(fn.Synthetic, "instantiation") { + reason = " (interp requires ssa.BuilderMode to include InstantiateGenerics on generics)" + } + panic("no code for function: " + name + reason) } } fr.env = make(map[ssa.Value]value) @@ -637,6 +642,9 @@ func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) { // gc does), or the argument to os.Exit for normal termination. // // The SSA program must include the "runtime" package. +// +// Type parameterized functions must have been built with +// InstantiateGenerics in the ssa.BuilderMode to be interpreted. func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) { i := &interpreter{ prog: mainpkg.Prog, diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go index 91ecb9793..4da3ffe0b 100644 --- a/go/ssa/interp/interp_test.go +++ b/go/ssa/interp/interp_test.go @@ -31,6 +31,7 @@ import ( "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/interp" "golang.org/x/tools/go/ssa/ssautil" + "golang.org/x/tools/internal/typeparams" ) // Each line contains a space-separated list of $GOROOT/test/ @@ -182,7 +183,9 @@ func run(t *testing.T, input string) bool { return false } - prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions) + bmode := ssa.InstantiateGenerics | ssa.SanityCheckFunctions + // bmode |= ssa.PrintFunctions // enable for debugging + prog := ssautil.CreateProgram(iprog, bmode) prog.Build() mainPkg := prog.Package(iprog.Created[0].Pkg) @@ -194,7 +197,10 @@ func run(t *testing.T, input string) bool { sizes := types.SizesFor("gc", ctx.GOARCH) hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -test -run --interp=T %s\n", input) - exitCode := interp.Interpret(mainPkg, 0, sizes, input, []string{}) + var imode interp.Mode // default mode + // imode |= interp.DisableRecover // enable for debugging + // imode |= interp.EnableTracing // enable for debugging + exitCode := interp.Interpret(mainPkg, imode, sizes, input, []string{}) if exitCode != 0 { t.Fatalf("interpreting %s: exit code was %d", input, exitCode) } @@ -248,3 +254,65 @@ func TestGorootTest(t *testing.T) { } printFailures(failures) } + +// TestTypeparamTest runs the interpreter on runnable examples +// in $GOROOT/test/typeparam/*.go. + +func TestTypeparamTest(t *testing.T) { + if !typeparams.Enabled { + return + } + + // Skip known failures for the given reason. + // TODO(taking): Address these. + skip := map[string]string{ + "chans.go": "interp tests do not support runtime.SetFinalizer", + "issue23536.go": "unknown reason", + "issue376214.go": "unknown issue with variadic cast on bytes", + "issue48042.go": "interp tests do not handle reflect.Value.SetInt", + "issue47716.go": "interp tests do not handle unsafe.Sizeof", + "issue50419.go": "interp tests do not handle dispatch to String() correctly", + "issue51733.go": "interp does not handle unsafe casts", + "ordered.go": "math.NaN() comparisons not being handled correctly", + "orderedmap.go": "interp tests do not support runtime.SetFinalizer", + "stringer.go": "unknown reason", + "issue48317.go": "interp tests do not support encoding/json", + "issue48318.go": "interp tests do not support encoding/json", + } + // Collect all of the .go files in dir that are runnable. + dir := filepath.Join(build.Default.GOROOT, "test", "typeparam") + list, err := os.ReadDir(dir) + if err != nil { + t.Fatal(err) + } + var inputs []string + for _, entry := range list { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { + continue // Consider standalone go files. + } + if reason := skip[entry.Name()]; reason != "" { + t.Logf("skipping %q due to %s.", entry.Name(), reason) + continue + } + input := filepath.Join(dir, entry.Name()) + src, err := os.ReadFile(input) + if err != nil { + t.Fatal(err) + } + // Only build test files that can be compiled, or compiled and run. + if bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// rundir")) { + inputs = append(inputs, input) + } else { + t.Logf("Not a `// run` file: %s", entry.Name()) + } + } + + var failures []string + for _, input := range inputs { + t.Log("running", input) + if !run(t, input) { + failures = append(failures, input) + } + } + printFailures(failures) +} diff --git a/go/ssa/interp/testdata/src/encoding/encoding.go b/go/ssa/interp/testdata/src/encoding/encoding.go new file mode 100644 index 000000000..73e9de494 --- /dev/null +++ b/go/ssa/interp/testdata/src/encoding/encoding.go @@ -0,0 +1,15 @@ +package encoding + +type BinaryMarshaler interface { + MarshalBinary() (data []byte, err error) +} +type BinaryUnmarshaler interface { + UnmarshalBinary(data []byte) error +} + +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/go/ssa/interp/testdata/src/log/log.go b/go/ssa/interp/testdata/src/log/log.go index 8897c1d21..9a57e8c1c 100644 --- a/go/ssa/interp/testdata/src/log/log.go +++ b/go/ssa/interp/testdata/src/log/log.go @@ -8,8 +8,16 @@ import ( func Println(v ...interface{}) { fmt.Println(v...) } +func Printf(format string, v ...interface{}) { + fmt.Printf(format, v...) +} func Fatalln(v ...interface{}) { Println(v...) os.Exit(1) } + +func Fatalf(format string, v ...interface{}) { + Printf(format, v...) + os.Exit(1) +} |