Source file test/rangegen.go

     1  // runoutput -goexperiment rangefunc
     2  
     3  // Copyright 2023 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  // Torture test for range-over-func.
     8  //
     9  // cmd/internal/testdir runs this like
    10  //
    11  //	go run rangegen.go >x.go
    12  //	go run x.go
    13  //
    14  // but a longer version can be run using
    15  //
    16  //	go run rangegen.go long
    17  //
    18  // In that second form, rangegen takes care of compiling
    19  // and running the code it generates, in batches.
    20  // That form takes 10-20 minutes to run.
    21  
    22  package main
    23  
    24  import (
    25  	"bytes"
    26  	"fmt"
    27  	"log"
    28  	"math/bits"
    29  	"os"
    30  	"os/exec"
    31  	"strings"
    32  )
    33  
    34  const verbose = false
    35  
    36  func main() {
    37  	long := len(os.Args) > 1 && os.Args[1] == "long"
    38  	log.SetFlags(0)
    39  	log.SetPrefix("rangegen: ")
    40  
    41  	if !long && bits.UintSize == 32 {
    42  		// Skip this test on 32-bit platforms, where it seems to
    43  		// cause timeouts and build problems.
    44  		skip()
    45  		return
    46  	}
    47  
    48  	b := new(bytes.Buffer)
    49  	tests := ""
    50  	flush := func(force bool) {
    51  		if !long || (strings.Count(tests, "\n") < 1000 && !force) {
    52  			return
    53  		}
    54  		p(b, mainCode, tests)
    55  		err := os.WriteFile("tmp.go", b.Bytes(), 0666)
    56  		if err != nil {
    57  			log.Fatal(err)
    58  		}
    59  		out, err := exec.Command("go", "run", "tmp.go").CombinedOutput()
    60  		if err != nil {
    61  			log.Fatalf("go run tmp.go: %v\n%s", err, out)
    62  		}
    63  		print(".")
    64  		if force {
    65  			print("\nPASS\n")
    66  		}
    67  		b.Reset()
    68  		tests = ""
    69  		p(b, "package main\n\n")
    70  		p(b, "const verbose = %v\n\n", verbose)
    71  	}
    72  
    73  	p(b, "package main\n\n")
    74  	p(b, "const verbose = %v\n\n", verbose)
    75  	max := 2
    76  	if !long {
    77  		max = 5
    78  	}
    79  	for i := 1; i <= max; i++ {
    80  		maxDouble := -1
    81  		if long {
    82  			maxDouble = i
    83  		}
    84  		for double := -1; double <= maxDouble; double++ {
    85  			code := gen(new(bytes.Buffer), "", "", "", i, double, func(c int) bool { return true })
    86  			for j := 0; j < code; j++ {
    87  				hi := j + 1
    88  				if long {
    89  					hi = code
    90  				}
    91  				for k := j; k < hi && k < code; k++ {
    92  					s := fmt.Sprintf("%d_%d_%d_%d", i, double+1, j, k)
    93  					code0 := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return c == j || c == k })
    94  					code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return c == j || c == k })
    95  					if code0 != code1 {
    96  						panic("bad generator")
    97  					}
    98  					tests += "test" + s + "()\n"
    99  					p(b, testCode, "test"+s, []int{j, k}, "testFunc"+s, "testSlice"+s)
   100  					flush(false)
   101  				}
   102  			}
   103  		}
   104  	}
   105  	for i := 1; i <= max; i++ {
   106  		maxDouble := -1
   107  		if long {
   108  			maxDouble = i
   109  		}
   110  		for double := -1; double <= maxDouble; double++ {
   111  			s := fmt.Sprintf("%d_%d", i, double+1)
   112  			code := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return true })
   113  			code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return true })
   114  			if code != code1 {
   115  				panic("bad generator")
   116  			}
   117  			tests += "test" + s + "()\n"
   118  			var all []int
   119  			for j := 0; j < code; j++ {
   120  				all = append(all, j)
   121  			}
   122  			p(b, testCode, "test"+s, all, "testFunc"+s, "testSlice"+s)
   123  			flush(false)
   124  		}
   125  	}
   126  	if long {
   127  		flush(true)
   128  		os.Remove("tmp.go")
   129  		return
   130  	}
   131  
   132  	p(b, mainCode, tests)
   133  
   134  	os.Stdout.Write(b.Bytes())
   135  }
   136  
   137  func p(b *bytes.Buffer, format string, args ...any) {
   138  	fmt.Fprintf(b, format, args...)
   139  }
   140  
   141  func gen(b *bytes.Buffer, name, prefix, rangeExpr string, depth, double int, allowed func(int) bool) int {
   142  	p(b, "func %s(o *output, code int) int {\n", name)
   143  	p(b, "	dfr := 0; _ = dfr\n")
   144  	code := genLoop(b, 0, prefix, rangeExpr, depth, double, 0, "", allowed)
   145  	p(b, "	return 0\n")
   146  	p(b, "}\n\n")
   147  	return code
   148  }
   149  
   150  func genLoop(b *bytes.Buffer, d int, prefix, rangeExpr string, depth, double, code int, labelSuffix string, allowed func(int) bool) int {
   151  	limit := 1
   152  	if d == double {
   153  		limit = 2
   154  	}
   155  	for rep := 0; rep < limit; rep++ {
   156  		if rep == 1 {
   157  			labelSuffix = "R"
   158  		}
   159  		s := fmt.Sprintf("%d%s", d, labelSuffix)
   160  		p(b, "	o.log(`top%s`)\n", s)
   161  		p(b, "	l%sa := 0\n", s)
   162  		p(b, "goto L%sa; L%sa:	o.log(`L%sa`)\n", s, s, s)
   163  		p(b, "	if l%sa++; l%sa >= 2 { o.log(`loop L%sa`); return -1 }\n", s, s, s)
   164  		p(b, "	l%sfor := 0\n", s)
   165  		p(b, "goto L%sfor; L%sfor: for f := 0; f < 1; f++ { o.log(`L%sfor`)\n", s, s, s)
   166  		p(b, "	if l%sfor++; l%sfor >= 2 { o.log(`loop L%sfor`); return -1 }\n", s, s, s)
   167  		p(b, "	l%ssw := 0\n", s)
   168  		p(b, "goto L%ssw; L%ssw: switch { default: o.log(`L%ssw`)\n", s, s, s)
   169  		p(b, "	if l%ssw++; l%ssw >= 2 { o.log(`loop L%ssw`); return -1 }\n", s, s, s)
   170  		p(b, "	l%ssel := 0\n", s)
   171  		p(b, "goto L%ssel; L%ssel: select { default: o.log(`L%ssel`)\n", s, s, s)
   172  		p(b, "	if l%ssel++; l%ssel >= 2 { o.log(`loop L%ssel`); return -1 }\n", s, s, s)
   173  		p(b, "	l%s := 0\n", s)
   174  		p(b, "goto L%s; L%s:	for %s i%s := range %s {\n", s, s, prefix, s, rangeExpr)
   175  		p(b, "	o.log1(`L%s top`, i%s)\n", s, s)
   176  		p(b, "	if l%s++; l%s >= 4 { o.log(`loop L%s`); return -1 }\n", s, s, s)
   177  		printTests := func() {
   178  			if code++; allowed(code) {
   179  				p(b, "	if code == %v { break }\n", code)
   180  			}
   181  			if code++; allowed(code) {
   182  				p(b, "	if code == %v { continue }\n", code)
   183  			}
   184  			if code++; allowed(code) {
   185  				p(b, "	switch { case code == %v: continue }\n", code)
   186  			}
   187  			if code++; allowed(code) {
   188  				p(b, "	if code == %v { return %[1]v }\n", code)
   189  			}
   190  			if code++; allowed(code) {
   191  				p(b, "	if code == %v { select { default: break } }\n", code)
   192  			}
   193  			if code++; allowed(code) {
   194  				p(b, "	if code == %v { switch { default: break } }\n", code)
   195  			}
   196  			if code++; allowed(code) {
   197  				p(b, "	if code == %v { dfr++; defer o.log1(`defer %d`, dfr) }\n", code, code)
   198  			}
   199  			for i := d; i > 0; i-- {
   200  				suffix := labelSuffix
   201  				if i < double {
   202  					suffix = ""
   203  				}
   204  				if code++; allowed(code) {
   205  					p(b, "	if code == %v { break L%d%s }\n", code, i, suffix)
   206  				}
   207  				if code++; allowed(code) {
   208  					p(b, "	if code == %v { select { default: break L%d%s } }\n", code, i, suffix)
   209  				}
   210  				if code++; allowed(code) {
   211  					p(b, "	if code == %v { break L%d%s }\n", code, i, suffix)
   212  				}
   213  				if code++; allowed(code) {
   214  					p(b, "	if code == %v { break L%d%ssw }\n", code, i, suffix)
   215  				}
   216  				if code++; allowed(code) {
   217  					p(b, "	if code == %v { break L%d%ssel }\n", code, i, suffix)
   218  				}
   219  				if code++; allowed(code) {
   220  					p(b, "	if code == %v { break L%d%sfor }\n", code, i, suffix)
   221  				}
   222  				if code++; allowed(code) {
   223  					p(b, "	if code == %v { continue L%d%sfor }\n", code, i, suffix)
   224  				}
   225  				if code++; allowed(code) {
   226  					p(b, "	if code == %v { goto L%d%sa }\n", code, i, suffix)
   227  				}
   228  				if code++; allowed(code) {
   229  					p(b, "	if code == %v { goto L%d%s }\n", code, i, suffix)
   230  				}
   231  				if code++; allowed(code) {
   232  					p(b, "	if code == %v { goto L%d%sb }\n", code, i, suffix)
   233  				}
   234  			}
   235  		}
   236  		printTests()
   237  		if d < depth {
   238  			if rep == 1 {
   239  				double = d // signal to children to use the rep=1 labels
   240  			}
   241  			code = genLoop(b, d+1, prefix, rangeExpr, depth, double, code, labelSuffix, allowed)
   242  			printTests()
   243  		}
   244  		p(b, "	o.log(`L%s bot`)\n", s)
   245  		p(b, "	}\n")
   246  		p(b, "	o.log(`L%ssel bot`)\n", s)
   247  		p(b, "	}\n")
   248  		p(b, "	o.log(`L%ssw bot`)\n", s)
   249  		p(b, "	}\n")
   250  		p(b, "	o.log(`L%sfor bot`)\n", s)
   251  		p(b, "	}\n")
   252  		p(b, "	o.log(`done%s`)\n", s)
   253  		p(b, "goto L%sb; L%sb: o.log(`L%sb`)\n", s, s, s)
   254  	}
   255  	return code
   256  }
   257  
   258  var testCode = `
   259  func %s() {
   260  	all := %#v
   261  	for i := 0; i < len(all); i++ {
   262  		c := all[i]
   263  		outFunc := run(%s, c)
   264  		outSlice := run(%s, c)
   265  		if !outFunc.eq(outSlice) {
   266  			println("mismatch", "%[3]s", "%[4]s", c)
   267  			println()
   268  			println("func:")
   269  			outFunc.print()
   270  			println()
   271  			println("slice:")
   272  			outSlice.print()
   273  			panic("mismatch")
   274  		}
   275  	}
   276  	if verbose {
   277  		println("did", "%[3]s", "%[4]s", len(all))
   278  	}
   279  }
   280  `
   281  
   282  var mainCode = `
   283  
   284  func main() {
   285  	if verbose {
   286  		println("main")
   287  	}
   288  	%s
   289  }
   290  
   291  func yield2(yield func(int)bool) { _ = yield(1) && yield(2) }
   292  var slice2 = []int{1,2}
   293  
   294  type output struct {
   295  	ret int
   296  	trace []any
   297  }
   298  
   299  func (o *output) log(x any) {
   300  	o.trace = append(o.trace, x)
   301  }
   302  
   303  func (o *output) log1(x, y any) {
   304  	o.trace = append(o.trace, x, y)
   305  }
   306  
   307  func (o *output) eq(p *output) bool{
   308  	if o.ret != p.ret  || len(o.trace) != len(p.trace) {
   309  		return false
   310  	}
   311  	for i ,x := range o.trace {
   312  		if x != p.trace[i] {
   313  			return false
   314  		}
   315  	}
   316  	return true
   317  }
   318  
   319  func (o *output) print() {
   320  	println("ret", o.ret, "trace-len", len(o.trace))
   321  	for i := 0; i < len(o.trace); i++ {
   322  		print("#", i, " ")
   323  		switch x := o.trace[i].(type) {
   324  		case int:
   325  			print(x)
   326  		case string:
   327  			print(x)
   328  		default:
   329  			print(x)
   330  		}
   331  		print("\n")
   332  	}
   333  }
   334  
   335  func run(f func(*output, int)int, i int) *output {
   336  	o := &output{}
   337  	o.ret = f(o, i)
   338  	return o
   339  }
   340  
   341  `
   342  
   343  func skip() {
   344  	const code = `
   345  package main
   346  func main() {
   347  }
   348  `
   349  	fmt.Printf("%s\n", code)
   350  }
   351  

View as plain text