]> Cypherpunks.ru repositories - gostls13.git/blobdiff - test/run.go
[dev.typeparams] all: merge master (c8f4e61) into dev.typeparams
[gostls13.git] / test / run.go
index d7f5d02391b67dbf2641af524889520c705761b3..ce0d645985ed9e2429e297ae76d7b9848e00d945 100644 (file)
@@ -42,11 +42,36 @@ var (
        linkshared     = flag.Bool("linkshared", false, "")
        updateErrors   = flag.Bool("update_errors", false, "update error messages in test file based on compiler output")
        runoutputLimit = flag.Int("l", defaultRunOutputLimit(), "number of parallel runoutput tests to run")
+       force          = flag.Bool("f", false, "ignore expected-failure test lists")
+       generics       = flag.String("G", defaultGLevels, "a comma-separated list of -G compiler flags to test with")
 
        shard  = flag.Int("shard", 0, "shard index to run. Only applicable if -shards is non-zero.")
        shards = flag.Int("shards", 0, "number of shards. If 0, all tests are run. This is used by the continuous build.")
 )
 
+var unifiedEnabled, defaultGLevels = func() (bool, string) {
+       // TODO(mdempsky): Change this to just "go env GOEXPERIMENT" after
+       // CL 328751 is merged back to dev.typeparams. In the mean time, we
+       // infer whether the "unified" experiment is default enabled by
+       // inspecting the output from `go tool compile -V`.
+       output := runOutput(goTool(), "tool", "compile", "-V")
+
+       // TODO(mdempsky): This will give false negatives if the unified
+       // experiment is enabled by default, but presumably at that point we
+       // won't need to disable tests for it anymore anyway.
+       enabled := strings.Contains(output, "unified")
+
+       // Normal test runs should test with both -G=0 and -G=3 for types2
+       // coverage. But the unified experiment always uses types2, so
+       // testing with -G=3 is redundant.
+       glevels := "0,3"
+       if enabled {
+               glevels = "0"
+       }
+
+       return enabled, glevels
+}()
+
 // defaultAllCodeGen returns the default value of the -all_codegen
 // flag. By default, we prefer to be fast (returning false), except on
 // the linux-amd64 builder that's already very fast, so we get more
@@ -61,7 +86,7 @@ var (
 
        // dirs are the directories to look for *.go files in.
        // TODO(bradfitz): just use all directories?
-       dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam"}
+       dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam", "typeparam/mdempsky"}
 
        // ratec controls the max number of tests running at a time.
        ratec chan bool
@@ -82,12 +107,20 @@ const maxTests = 5000
 func main() {
        flag.Parse()
 
+       var glevels []int
+       for _, s := range strings.Split(*generics, ",") {
+               glevel, err := strconv.Atoi(s)
+               if err != nil {
+                       log.Fatalf("invalid -G flag: %v", err)
+               }
+               glevels = append(glevels, glevel)
+       }
+
        goos = getenv("GOOS", runtime.GOOS)
        goarch = getenv("GOARCH", runtime.GOARCH)
-       cgoEnv, err := exec.Command(goTool(), "env", "CGO_ENABLED").Output()
-       if err == nil {
-               cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(cgoEnv)))
-       }
+
+       cgoEnv := runOutput(goTool(), "env", "CGO_ENABLED")
+       cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(cgoEnv))
 
        findExecCmd()
 
@@ -113,11 +146,11 @@ func main() {
                        }
                        if fi, err := os.Stat(arg); err == nil && fi.IsDir() {
                                for _, baseGoFile := range goFiles(arg) {
-                                       tests = append(tests, startTest(arg, baseGoFile))
+                                       tests = append(tests, startTests(arg, baseGoFile, glevels)...)
                                }
                        } else if strings.HasSuffix(arg, ".go") {
                                dir, file := filepath.Split(arg)
-                               tests = append(tests, startTest(dir, file))
+                               tests = append(tests, startTests(dir, file, glevels)...)
                        } else {
                                log.Fatalf("can't yet deal with non-directory and non-go file %q", arg)
                        }
@@ -125,7 +158,7 @@ func main() {
        } else {
                for _, dir := range dirs {
                        for _, baseGoFile := range goFiles(dir) {
-                               tests = append(tests, startTest(dir, baseGoFile))
+                               tests = append(tests, startTests(dir, baseGoFile, glevels)...)
                        }
                }
        }
@@ -142,8 +175,15 @@ func main() {
                        status = "FAIL"
                }
                if test.err != nil {
-                       status = "FAIL"
                        errStr = test.err.Error()
+                       if test.expectFail {
+                               errStr += " (expected)"
+                       } else {
+                               status = "FAIL"
+                       }
+               } else if test.expectFail {
+                       status = "FAIL"
+                       errStr = "unexpected success"
                }
                if status == "FAIL" {
                        failed = true
@@ -151,7 +191,8 @@ func main() {
                resCount[status]++
                dt := fmt.Sprintf("%.3fs", test.dt.Seconds())
                if status == "FAIL" {
-                       fmt.Printf("# go run run.go -- %s\n%s\nFAIL\t%s\t%s\n",
+                       fmt.Printf("# go run run.go -G=%v %s\n%s\nFAIL\t%s\t%s\n",
+                               test.glevel,
                                path.Join(test.dir, test.gofile),
                                errStr, test.goFileName(), dt)
                        continue
@@ -173,6 +214,17 @@ func main() {
        }
 }
 
+// runOutput runs the specified command and returns its output as a
+// string. If the command fails, runOutput logs the error and exits.
+func runOutput(name string, args ...string) string {
+       cmd := exec.Command(name, args...)
+       output, err := cmd.Output()
+       if err != nil {
+               log.Fatalf("running %v: %v", cmd, err)
+       }
+       return string(output)
+}
+
 // goTool reports the path of the go tool to use to run the tests.
 // If possible, use the same Go used to run run.go, otherwise
 // fallback to the go version found in the PATH.
@@ -270,30 +322,73 @@ type test struct {
        dir, gofile string
        donec       chan bool // closed when done
        dt          time.Duration
+       glevel      int // what -G level this test should use
 
        src string
 
        tempDir string
        err     error
+
+       // expectFail indicates whether the (overall) test recipe is
+       // expected to fail under the current test configuration (e.g., -G=3
+       // or GOEXPERIMENT=unified).
+       expectFail bool
 }
 
-// startTest
-func startTest(dir, gofile string) *test {
-       t := &test{
-               dir:    dir,
-               gofile: gofile,
-               donec:  make(chan bool, 1),
+// initExpectFail initializes t.expectFail based on the build+test
+// configuration. It should only be called for tests known to use
+// types2.
+func (t *test) initExpectFail() {
+       if *force {
+               return
        }
-       if toRun == nil {
-               toRun = make(chan *test, maxTests)
-               go runTests()
+
+       failureSets := []map[string]bool{types2Failures}
+
+       // Note: gccgo supports more 32-bit architectures than this, but
+       // hopefully the 32-bit failures are fixed before this matters.
+       switch goarch {
+       case "386", "arm", "mips", "mipsle":
+               failureSets = append(failureSets, types2Failures32Bit)
        }
-       select {
-       case toRun <- t:
-       default:
-               panic("toRun buffer size (maxTests) is too small")
+
+       if unifiedEnabled {
+               failureSets = append(failureSets, unifiedFailures)
+       } else {
+               failureSets = append(failureSets, g3Failures)
+       }
+
+       filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows
+
+       for _, set := range failureSets {
+               if set[filename] {
+                       t.expectFail = true
+                       return
+               }
        }
-       return t
+}
+
+func startTests(dir, gofile string, glevels []int) []*test {
+       tests := make([]*test, len(glevels))
+       for i, glevel := range glevels {
+               t := &test{
+                       dir:    dir,
+                       gofile: gofile,
+                       glevel: glevel,
+                       donec:  make(chan bool, 1),
+               }
+               if toRun == nil {
+                       toRun = make(chan *test, maxTests)
+                       go runTests()
+               }
+               select {
+               case toRun <- t:
+               default:
+                       panic("toRun buffer size (maxTests) is too small")
+               }
+               tests[i] = t
+       }
+       return tests
 }
 
 // runTests runs tests in parallel, but respecting the order they
@@ -480,12 +575,16 @@ func init() { checkShouldTest() }
 // This must match the flags used for building the standard library,
 // or else the commands will rebuild any needed packages (like runtime)
 // over and over.
-func goGcflags() string {
-       return "-gcflags=all=" + os.Getenv("GO_GCFLAGS")
+func (t *test) goGcflags() string {
+       flags := os.Getenv("GO_GCFLAGS")
+       if t.glevel != 0 {
+               flags = fmt.Sprintf("%s -G=%v", flags, t.glevel)
+       }
+       return "-gcflags=all=" + flags
 }
 
-func goGcflagsIsEmpty() bool {
-       return "" == os.Getenv("GO_GCFLAGS")
+func (t *test) goGcflagsIsEmpty() bool {
+       return "" == os.Getenv("GO_GCFLAGS") && t.glevel == 0
 }
 
 var errTimeout = errors.New("command exceeded time limit")
@@ -541,7 +640,11 @@ func (t *test) run() {
        singlefilepkgs := false
        setpkgpaths := false
        localImports := true
-       f := strings.Fields(action)
+       f, err := splitQuoted(action)
+       if err != nil {
+               t.err = fmt.Errorf("invalid test recipe: %v", err)
+               return
+       }
        if len(f) > 0 {
                action = f[0]
                args = f[1:]
@@ -616,6 +719,63 @@ func (t *test) run() {
                }
        }
 
+       type Tool int
+
+       const (
+               _ Tool = iota
+               AsmCheck
+               Build
+               Run
+               Compile
+       )
+
+       // validForGLevel reports whether the current test is valid to run
+       // at the specified -G level. If so, it may update flags as
+       // necessary to test with -G.
+       validForGLevel := func(tool Tool) bool {
+               hasGFlag := false
+               for _, flag := range flags {
+                       if strings.Contains(flag, "-G") {
+                               hasGFlag = true
+                       }
+               }
+
+               if hasGFlag && t.glevel != 0 {
+                       // test provides explicit -G flag already; don't run again
+                       if *verbose {
+                               fmt.Printf("excl\t%s\n", t.goFileName())
+                       }
+                       return false
+               }
+
+               if t.glevel == 0 && !hasGFlag && !unifiedEnabled {
+                       // tests should always pass when run w/o types2 (i.e., using the
+                       // legacy typechecker).
+                       return true
+               }
+
+               t.initExpectFail()
+
+               switch tool {
+               case Build, Run:
+                       // ok; handled in goGcflags
+
+               case Compile:
+                       if !hasGFlag {
+                               flags = append(flags, fmt.Sprintf("-G=%v", t.glevel))
+                       }
+
+               default:
+                       // we don't know how to add -G for this test yet
+                       if *verbose {
+                               fmt.Printf("excl\t%s\n", t.goFileName())
+                       }
+                       return false
+               }
+
+               return true
+       }
+
        t.makeTempDir()
        if !*keep {
                defer os.RemoveAll(t.tempDir)
@@ -692,6 +852,10 @@ func (t *test) run() {
                t.err = fmt.Errorf("unimplemented action %q", action)
 
        case "asmcheck":
+               if !validForGLevel(AsmCheck) {
+                       return
+               }
+
                // Compile Go file and match the generated assembly
                // against a set of regexps in comments.
                ops := t.wantedAsmOpcodes(long)
@@ -746,6 +910,10 @@ func (t *test) run() {
                return
 
        case "errorcheck":
+               if !validForGLevel(Compile) {
+                       return
+               }
+
                // Compile Go file.
                // Fail if wantError is true and compilation was successful and vice versa.
                // Match errors produced by gc against errors in comments.
@@ -774,72 +942,20 @@ func (t *test) run() {
                        t.updateErrors(string(out), long)
                }
                t.err = t.errorCheck(string(out), wantAuto, long, t.gofile)
-               if t.err != nil {
-                       return // don't hide error if run below succeeds
-               }
-
-               // The following is temporary scaffolding to get types2 typechecker
-               // up and running against the existing test cases. The explicitly
-               // listed files don't pass yet, usually because the error messages
-               // are slightly different (this list is not complete). Any errorcheck
-               // tests that require output from analysis phases past initial type-
-               // checking are also excluded since these phases are not running yet.
-               // We can get rid of this code once types2 is fully plugged in.
-
-               // For now we're done when we can't handle the file or some of the flags.
-               // The first goal is to eliminate the excluded list; the second goal is to
-               // eliminate the flag list.
-
-               // Excluded files.
-               filename := strings.Replace(t.goFileName(), "\\", "/", -1) // goFileName() uses \ on Windows
-               if excluded[filename] {
-                       if *verbose {
-                               fmt.Printf("excl\t%s\n", filename)
-                       }
-                       return // cannot handle file yet
-               }
-
-               // Excluded flags.
-               for _, flag := range flags {
-                       for _, pattern := range []string{
-                               "-m",
-                       } {
-                               if strings.Contains(flag, pattern) {
-                                       if *verbose {
-                                               fmt.Printf("excl\t%s\t%s\n", filename, flags)
-                                       }
-                                       return // cannot handle flag
-                               }
-                       }
-               }
 
-               // Run errorcheck again with -G option (new typechecker).
-               cmdline = []string{goTool(), "tool", "compile", "-G=3", "-C", "-e", "-o", "a.o"}
-               // No need to add -dynlink even if linkshared if we're just checking for errors...
-               cmdline = append(cmdline, flags...)
-               cmdline = append(cmdline, long)
-               out, err = runcmd(cmdline...)
-               if wantError {
-                       if err == nil {
-                               t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
-                               return
-                       }
-               } else {
-                       if err != nil {
-                               t.err = err
-                               return
-                       }
-               }
-               if *updateErrors {
-                       t.updateErrors(string(out), long)
+       case "compile":
+               if !validForGLevel(Compile) {
+                       return
                }
-               t.err = t.errorCheck(string(out), wantAuto, long, t.gofile)
 
-       case "compile":
                // Compile Go file.
                _, t.err = compileFile(runcmd, long, flags)
 
        case "compiledir":
+               if !validForGLevel(Compile) {
+                       return
+               }
+
                // Compile all files in the directory as packages in lexicographic order.
                longdir := filepath.Join(cwd, t.goDirName())
                pkgs, err := goDirPackages(longdir, singlefilepkgs)
@@ -855,6 +971,10 @@ func (t *test) run() {
                }
 
        case "errorcheckdir", "errorcheckandrundir":
+               if !validForGLevel(Compile) {
+                       return
+               }
+
                flags = append(flags, "-d=panic")
                // Compile and errorCheck all files in the directory as packages in lexicographic order.
                // If errorcheckdir and wantError, compilation of the last package must fail.
@@ -900,6 +1020,10 @@ func (t *test) run() {
                fallthrough
 
        case "rundir":
+               if !validForGLevel(Run) {
+                       return
+               }
+
                // Compile all files in the directory as packages in lexicographic order.
                // In case of errorcheckandrundir, ignore failed compilation of the package before the last.
                // Link as if the last file is the main package, run it.
@@ -958,6 +1082,10 @@ func (t *test) run() {
                }
 
        case "runindir":
+               if !validForGLevel(Run) {
+                       return
+               }
+
                // Make a shallow copy of t.goDirName() in its own module and GOPATH, and
                // run "go run ." in it. The module path (and hence import path prefix) of
                // the copy is equal to the basename of the source directory.
@@ -983,7 +1111,7 @@ func (t *test) run() {
                        return
                }
 
-               cmd := []string{goTool(), "run", goGcflags()}
+               cmd := []string{goTool(), "run", t.goGcflags()}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
                }
@@ -997,13 +1125,21 @@ func (t *test) run() {
                t.checkExpectedOutput(out)
 
        case "build":
+               if !validForGLevel(Build) {
+                       return
+               }
+
                // Build Go file.
-               _, err := runcmd(goTool(), "build", goGcflags(), "-o", "a.exe", long)
+               _, err := runcmd(goTool(), "build", t.goGcflags(), "-o", "a.exe", long)
                if err != nil {
                        t.err = err
                }
 
        case "builddir", "buildrundir":
+               if !validForGLevel(Build) {
+                       return
+               }
+
                // Build an executable from all the .go and .s files in a subdirectory.
                // Run it and verify its output in the buildrundir case.
                longdir := filepath.Join(cwd, t.goDirName())
@@ -1083,10 +1219,14 @@ func (t *test) run() {
                }
 
        case "buildrun":
+               if !validForGLevel(Build) {
+                       return
+               }
+
                // Build an executable from Go file, then run it, verify its output.
                // Useful for timeout tests where failure mode is infinite loop.
                // TODO: not supported on NaCl
-               cmd := []string{goTool(), "build", goGcflags(), "-o", "a.exe"}
+               cmd := []string{goTool(), "build", t.goGcflags(), "-o", "a.exe"}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
                }
@@ -1108,13 +1248,17 @@ func (t *test) run() {
                t.checkExpectedOutput(out)
 
        case "run":
+               if !validForGLevel(Run) {
+                       return
+               }
+
                // Run Go file if no special go command flags are provided;
                // otherwise build an executable and run it.
                // Verify the output.
                runInDir = ""
                var out []byte
                var err error
-               if len(flags)+len(args) == 0 && goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS {
+               if len(flags)+len(args) == 0 && t.goGcflagsIsEmpty() && !*linkshared && goarch == runtime.GOARCH && goos == runtime.GOOS {
                        // If we're not using special go command flags,
                        // skip all the go command machinery.
                        // This avoids any time the go command would
@@ -1136,7 +1280,7 @@ func (t *test) run() {
                        }
                        out, err = runcmd(append([]string{exe}, args...)...)
                } else {
-                       cmd := []string{goTool(), "run", goGcflags()}
+                       cmd := []string{goTool(), "run", t.goGcflags()}
                        if *linkshared {
                                cmd = append(cmd, "-linkshared")
                        }
@@ -1151,6 +1295,10 @@ func (t *test) run() {
                t.checkExpectedOutput(out)
 
        case "runoutput":
+               if !validForGLevel(Run) {
+                       return
+               }
+
                // Run Go file and write its output into temporary Go file.
                // Run generated Go file and verify its output.
                rungatec <- true
@@ -1158,7 +1306,7 @@ func (t *test) run() {
                        <-rungatec
                }()
                runInDir = ""
-               cmd := []string{goTool(), "run", goGcflags()}
+               cmd := []string{goTool(), "run", t.goGcflags()}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
                }
@@ -1173,7 +1321,7 @@ func (t *test) run() {
                        t.err = fmt.Errorf("write tempfile:%s", err)
                        return
                }
-               cmd = []string{goTool(), "run", goGcflags()}
+               cmd = []string{goTool(), "run", t.goGcflags()}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
                }
@@ -1186,10 +1334,14 @@ func (t *test) run() {
                t.checkExpectedOutput(out)
 
        case "errorcheckoutput":
+               if !validForGLevel(Compile) {
+                       return
+               }
+
                // Run Go file and write its output into temporary Go file.
                // Compile and errorCheck generated Go file.
                runInDir = ""
-               cmd := []string{goTool(), "run", goGcflags()}
+               cmd := []string{goTool(), "run", t.goGcflags()}
                if *linkshared {
                        cmd = append(cmd, "-linkshared")
                }
@@ -1941,66 +2093,163 @@ func overlayDir(dstRoot, srcRoot string) error {
        })
 }
 
+// The following is temporary scaffolding to get types2 typechecker
+// up and running against the existing test cases. The explicitly
+// listed files don't pass yet, usually because the error messages
+// are slightly different (this list is not complete). Any errorcheck
+// tests that require output from analysis phases past initial type-
+// checking are also excluded since these phases are not running yet.
+// We can get rid of this code once types2 is fully plugged in.
+
 // List of files that the compiler cannot errorcheck with the new typechecker (compiler -G option).
 // Temporary scaffolding until we pass all the tests at which point this map can be removed.
-var excluded = map[string]bool{
-       "complit1.go":     true, // types2 reports extra errors
-       "const2.go":       true, // types2 not run after syntax errors
-       "ddd1.go":         true, // issue #42987
-       "directive.go":    true, // misplaced compiler directive checks
-       "float_lit3.go":   true, // types2 reports extra errors
-       "import1.go":      true, // types2 reports extra errors
-       "import5.go":      true, // issue #42988
-       "import6.go":      true, // issue #43109
-       "initializerr.go": true, // types2 reports extra errors
-       "linkname2.go":    true, // error reported by noder (not running for types2 errorcheck test)
-       "notinheap.go":    true, // types2 doesn't report errors about conversions that are invalid due to //go:notinheap
-       "shift1.go":       true, // issue #42989
-       "typecheck.go":    true, // invalid function is not causing errors when called
-       "writebarrier.go": true, // correct diagnostics, but different lines (probably irgen's fault)
-
-       "fixedbugs/bug176.go":    true, // types2 reports all errors (pref: types2)
-       "fixedbugs/bug195.go":    true, // types2 reports slightly different (but correct) bugs
-       "fixedbugs/bug228.go":    true, // types2 not run after syntax errors
-       "fixedbugs/bug231.go":    true, // types2 bug? (same error reported twice)
-       "fixedbugs/bug255.go":    true, // types2 reports extra errors
-       "fixedbugs/bug351.go":    true, // types2 reports extra errors
-       "fixedbugs/bug374.go":    true, // types2 reports extra errors
-       "fixedbugs/bug385_32.go": true, // types2 doesn't produce missing error "type .* too large" (32-bit specific)
-       "fixedbugs/bug388.go":    true, // types2 not run due to syntax errors
-       "fixedbugs/bug412.go":    true, // types2 produces a follow-on error
-
-       "fixedbugs/issue11590.go":  true, // types2 doesn't report a follow-on error (pref: types2)
-       "fixedbugs/issue11610.go":  true, // types2 not run after syntax errors
-       "fixedbugs/issue11614.go":  true, // types2 reports an extra error
-       "fixedbugs/issue13415.go":  true, // declared but not used conflict
-       "fixedbugs/issue14520.go":  true, // missing import path error by types2
-       "fixedbugs/issue16428.go":  true, // types2 reports two instead of one error
-       "fixedbugs/issue17038.go":  true, // types2 doesn't report a follow-on error (pref: types2)
-       "fixedbugs/issue17645.go":  true, // multiple errors on same line
-       "fixedbugs/issue18331.go":  true, // missing error about misuse of //go:noescape (irgen needs code from noder)
-       "fixedbugs/issue18393.go":  true, // types2 not run after syntax errors
-       "fixedbugs/issue19012.go":  true, // multiple errors on same line
-       "fixedbugs/issue20233.go":  true, // types2 reports two instead of one error (pref: compiler)
-       "fixedbugs/issue20245.go":  true, // types2 reports two instead of one error (pref: compiler)
-       "fixedbugs/issue20250.go":  true, // correct diagnostics, but different lines (probably irgen's fault)
-       "fixedbugs/issue21979.go":  true, // types2 doesn't report a follow-on error (pref: types2)
-       "fixedbugs/issue23732.go":  true, // types2 reports different (but ok) line numbers
-       "fixedbugs/issue25958.go":  true, // types2 doesn't report a follow-on error (pref: types2)
-       "fixedbugs/issue28079b.go": true, // types2 reports follow-on errors
-       "fixedbugs/issue28268.go":  true, // types2 reports follow-on errors
-       "fixedbugs/issue33460.go":  true, // types2 reports alternative positions in separate error
-       "fixedbugs/issue41575.go":  true, // types2 reports alternative positions in separate error
-       "fixedbugs/issue42058a.go": true, // types2 doesn't report "channel element type too large"
-       "fixedbugs/issue42058b.go": true, // types2 doesn't report "channel element type too large"
-       "fixedbugs/issue4232.go":   true, // types2 reports (correct) extra errors
-       "fixedbugs/issue4452.go":   true, // types2 reports (correct) extra errors
-       "fixedbugs/issue5609.go":   true, // types2 needs a better error message
-       "fixedbugs/issue6889.go":   true, // types2 can handle this without constant overflow
-       "fixedbugs/issue7525.go":   true, // types2 reports init cycle error on different line - ok otherwise
-       "fixedbugs/issue7525b.go":  true, // types2 reports init cycle error on different line - ok otherwise
-       "fixedbugs/issue7525c.go":  true, // types2 reports init cycle error on different line - ok otherwise
-       "fixedbugs/issue7525d.go":  true, // types2 reports init cycle error on different line - ok otherwise
-       "fixedbugs/issue7525e.go":  true, // types2 reports init cycle error on different line - ok otherwise
-       "fixedbugs/issue46749.go":  true, // types2 reports can not convert error instead of type mismatched
+var types2Failures = setOf(
+       "directive.go",    // misplaced compiler directive checks
+       "float_lit3.go",   // types2 reports extra errors
+       "import1.go",      // types2 reports extra errors
+       "import6.go",      // issue #43109
+       "initializerr.go", // types2 reports extra errors
+       "linkname2.go",    // error reported by noder (not running for types2 errorcheck test)
+       "notinheap.go",    // types2 doesn't report errors about conversions that are invalid due to //go:notinheap
+       "shift1.go",       // issue #42989
+       "typecheck.go",    // invalid function is not causing errors when called
+
+       "interface/private.go", // types2 phrases errors differently (doesn't use non-spec "private" term)
+
+       "fixedbugs/bug176.go", // types2 reports all errors (pref: types2)
+       "fixedbugs/bug195.go", // types2 reports slightly different (but correct) bugs
+       "fixedbugs/bug228.go", // types2 doesn't run when there are syntax errors
+       "fixedbugs/bug231.go", // types2 bug? (same error reported twice)
+       "fixedbugs/bug255.go", // types2 reports extra errors
+       "fixedbugs/bug374.go", // types2 reports extra errors
+       "fixedbugs/bug388.go", // types2 not run due to syntax errors
+       "fixedbugs/bug412.go", // types2 produces a follow-on error
+
+       "fixedbugs/issue10700.go",  // types2 reports ok hint, but does not match regexp
+       "fixedbugs/issue11590.go",  // types2 doesn't report a follow-on error (pref: types2)
+       "fixedbugs/issue11610.go",  // types2 not run after syntax errors
+       "fixedbugs/issue11614.go",  // types2 reports an extra error
+       "fixedbugs/issue14520.go",  // missing import path error by types2
+       "fixedbugs/issue16133.go",  // types2 doesn't use package path for qualified identifiers when package name is ambiguous
+       "fixedbugs/issue16428.go",  // types2 reports two instead of one error
+       "fixedbugs/issue17038.go",  // types2 doesn't report a follow-on error (pref: types2)
+       "fixedbugs/issue17645.go",  // multiple errors on same line
+       "fixedbugs/issue18331.go",  // missing error about misuse of //go:noescape (irgen needs code from noder)
+       "fixedbugs/issue18419.go",  // types2 reports
+       "fixedbugs/issue19012.go",  // multiple errors on same line
+       "fixedbugs/issue20233.go",  // types2 reports two instead of one error (pref: compiler)
+       "fixedbugs/issue20245.go",  // types2 reports two instead of one error (pref: compiler)
+       "fixedbugs/issue21979.go",  // types2 doesn't report a follow-on error (pref: types2)
+       "fixedbugs/issue23732.go",  // types2 reports different (but ok) line numbers
+       "fixedbugs/issue25958.go",  // types2 doesn't report a follow-on error (pref: types2)
+       "fixedbugs/issue28079b.go", // types2 reports follow-on errors
+       "fixedbugs/issue28268.go",  // types2 reports follow-on errors
+       "fixedbugs/issue31053.go",  // types2 reports "unknown field" instead of "cannot refer to unexported field"
+       "fixedbugs/issue33460.go",  // types2 reports alternative positions in separate error
+       "fixedbugs/issue42058a.go", // types2 doesn't report "channel element type too large"
+       "fixedbugs/issue42058b.go", // types2 doesn't report "channel element type too large"
+       "fixedbugs/issue4232.go",   // types2 reports (correct) extra errors
+       "fixedbugs/issue4452.go",   // types2 reports (correct) extra errors
+       "fixedbugs/issue4510.go",   // types2 reports different (but ok) line numbers
+       "fixedbugs/issue47201.go",  // types2 spells the error message differently
+       "fixedbugs/issue5609.go",   // types2 needs a better error message
+       "fixedbugs/issue7525b.go",  // types2 reports init cycle error on different line - ok otherwise
+       "fixedbugs/issue7525c.go",  // types2 reports init cycle error on different line - ok otherwise
+       "fixedbugs/issue7525d.go",  // types2 reports init cycle error on different line - ok otherwise
+       "fixedbugs/issue7525e.go",  // types2 reports init cycle error on different line - ok otherwise
+       "fixedbugs/issue7525.go",   // types2 reports init cycle error on different line - ok otherwise
+)
+
+var types2Failures32Bit = setOf(
+       "printbig.go",             // large untyped int passed to print (32-bit)
+       "fixedbugs/bug114.go",     // large untyped int passed to println (32-bit)
+       "fixedbugs/issue23305.go", // large untyped int passed to println (32-bit)
+       "fixedbugs/bug385_32.go",  // types2 doesn't produce missing error "type .* too large" (32-bit specific)
+)
+
+var g3Failures = setOf(
+       "writebarrier.go", // correct diagnostics, but different lines (probably irgen's fault)
+
+       "typeparam/nested.go", // -G=3 doesn't support function-local types with generics
+
+       "typeparam/mdempsky/4.go", // -G=3 can't export functions with labeled breaks in loops
+)
+
+var unifiedFailures = setOf(
+       "closure3.go", // unified IR numbers closures differently than -d=inlfuncswithclosures
+       "escape4.go",  // unified IR can inline f5 and f6; test doesn't expect this
+       "inline.go",   // unified IR reports function literal diagnostics on different lines than -d=inlfuncswithclosures
+
+       "fixedbugs/issue42284.go", // prints "T(0) does not escape", but test expects "a.I(a.T(0)) does not escape"
+       "fixedbugs/issue7921.go",  // prints "… escapes to heap", but test expects "string(…) escapes to heap"
+)
+
+func setOf(keys ...string) map[string]bool {
+       m := make(map[string]bool, len(keys))
+       for _, key := range keys {
+               m[key] = true
+       }
+       return m
+}
+
+// splitQuoted splits the string s around each instance of one or more consecutive
+// white space characters while taking into account quotes and escaping, and
+// returns an array of substrings of s or an empty list if s contains only white space.
+// Single quotes and double quotes are recognized to prevent splitting within the
+// quoted region, and are removed from the resulting substrings. If a quote in s
+// isn't closed err will be set and r will have the unclosed argument as the
+// last element. The backslash is used for escaping.
+//
+// For example, the following string:
+//
+//     a b:"c d" 'e''f'  "g\""
+//
+// Would be parsed as:
+//
+//     []string{"a", "b:c d", "ef", `g"`}
+//
+// [copied from src/go/build/build.go]
+func splitQuoted(s string) (r []string, err error) {
+       var args []string
+       arg := make([]rune, len(s))
+       escaped := false
+       quoted := false
+       quote := '\x00'
+       i := 0
+       for _, rune := range s {
+               switch {
+               case escaped:
+                       escaped = false
+               case rune == '\\':
+                       escaped = true
+                       continue
+               case quote != '\x00':
+                       if rune == quote {
+                               quote = '\x00'
+                               continue
+                       }
+               case rune == '"' || rune == '\'':
+                       quoted = true
+                       quote = rune
+                       continue
+               case unicode.IsSpace(rune):
+                       if quoted || i > 0 {
+                               quoted = false
+                               args = append(args, string(arg[:i]))
+                               i = 0
+                       }
+                       continue
+               }
+               arg[i] = rune
+               i++
+       }
+       if quoted || i > 0 {
+               args = append(args, string(arg[:i]))
+       }
+       if quote != 0 {
+               err = errors.New("unclosed quote")
+       } else if escaped {
+               err = errors.New("unfinished escaping")
+       }
+       return args, err
 }