1 // runoutput -goexperiment rangefunc
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.
7 // Torture test for range-over-func.
9 // cmd/internal/testdir runs this like
11 // go run rangegen.go >x.go
14 // but a longer version can be run using
16 // go run rangegen.go long
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.
36 long := len(os.Args) > 1 && os.Args[1] == "long"
38 log.SetPrefix("rangegen: ")
40 b := new(bytes.Buffer)
42 flush := func(force bool) {
43 if !long || (strings.Count(tests, "\n") < 1000 && !force) {
47 err := os.WriteFile("tmp.go", b.Bytes(), 0666)
51 out, err := exec.Command("go", "run", "tmp.go").CombinedOutput()
53 log.Fatalf("go run tmp.go: %v\n%s", err, out)
61 p(b, "package main\n\n")
62 p(b, "const verbose = %v\n\n", verbose)
65 p(b, "package main\n\n")
66 p(b, "const verbose = %v\n\n", verbose)
71 for i := 1; i <= max; i++ {
76 for double := -1; double <= maxDouble; double++ {
77 code := gen(new(bytes.Buffer), "", "", "", i, double, func(c int) bool { return true })
78 for j := 0; j < code; j++ {
83 for k := j; k < hi && k < code; k++ {
84 s := fmt.Sprintf("%d_%d_%d_%d", i, double+1, j, k)
85 code0 := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return c == j || c == k })
86 code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return c == j || c == k })
88 panic("bad generator")
90 tests += "test" + s + "()\n"
91 p(b, testCode, "test"+s, []int{j, k}, "testFunc"+s, "testSlice"+s)
97 for i := 1; i <= max; i++ {
102 for double := -1; double <= maxDouble; double++ {
103 s := fmt.Sprintf("%d_%d", i, double+1)
104 code := gen(b, "testFunc"+s, "", "yield2", i, double, func(c int) bool { return true })
105 code1 := gen(b, "testSlice"+s, "_, ", "slice2", i, double, func(c int) bool { return true })
107 panic("bad generator")
109 tests += "test" + s + "()\n"
111 for j := 0; j < code; j++ {
114 p(b, testCode, "test"+s, all, "testFunc"+s, "testSlice"+s)
124 p(b, mainCode, tests)
126 os.Stdout.Write(b.Bytes())
129 func p(b *bytes.Buffer, format string, args ...any) {
130 fmt.Fprintf(b, format, args...)
133 func gen(b *bytes.Buffer, name, prefix, rangeExpr string, depth, double int, allowed func(int) bool) int {
134 p(b, "func %s(o *output, code int) int {\n", name)
135 p(b, " dfr := 0; _ = dfr\n")
136 code := genLoop(b, 0, prefix, rangeExpr, depth, double, 0, "", allowed)
142 func genLoop(b *bytes.Buffer, d int, prefix, rangeExpr string, depth, double, code int, labelSuffix string, allowed func(int) bool) int {
147 for rep := 0; rep < limit; rep++ {
151 s := fmt.Sprintf("%d%s", d, labelSuffix)
152 p(b, " o.log(`top%s`)\n", s)
153 p(b, " l%sa := 0\n", s)
154 p(b, "goto L%sa; L%sa: o.log(`L%sa`)\n", s, s, s)
155 p(b, " if l%sa++; l%sa >= 2 { o.log(`loop L%sa`); return -1 }\n", s, s, s)
156 p(b, " l%sfor := 0\n", s)
157 p(b, "goto L%sfor; L%sfor: for f := 0; f < 1; f++ { o.log(`L%sfor`)\n", s, s, s)
158 p(b, " if l%sfor++; l%sfor >= 2 { o.log(`loop L%sfor`); return -1 }\n", s, s, s)
159 p(b, " l%ssw := 0\n", s)
160 p(b, "goto L%ssw; L%ssw: switch { default: o.log(`L%ssw`)\n", s, s, s)
161 p(b, " if l%ssw++; l%ssw >= 2 { o.log(`loop L%ssw`); return -1 }\n", s, s, s)
162 p(b, " l%ssel := 0\n", s)
163 p(b, "goto L%ssel; L%ssel: select { default: o.log(`L%ssel`)\n", s, s, s)
164 p(b, " if l%ssel++; l%ssel >= 2 { o.log(`loop L%ssel`); return -1 }\n", s, s, s)
165 p(b, " l%s := 0\n", s)
166 p(b, "goto L%s; L%s: for %s i%s := range %s {\n", s, s, prefix, s, rangeExpr)
167 p(b, " o.log1(`L%s top`, i%s)\n", s, s)
168 p(b, " if l%s++; l%s >= 4 { o.log(`loop L%s`); return -1 }\n", s, s, s)
169 printTests := func() {
170 if code++; allowed(code) {
171 p(b, " if code == %v { break }\n", code)
173 if code++; allowed(code) {
174 p(b, " if code == %v { continue }\n", code)
176 if code++; allowed(code) {
177 p(b, " switch { case code == %v: continue }\n", code)
179 if code++; allowed(code) {
180 p(b, " if code == %v { return %[1]v }\n", code)
182 if code++; allowed(code) {
183 p(b, " if code == %v { select { default: break } }\n", code)
185 if code++; allowed(code) {
186 p(b, " if code == %v { switch { default: break } }\n", code)
188 if code++; allowed(code) {
189 p(b, " if code == %v { dfr++; defer o.log1(`defer %d`, dfr) }\n", code, code)
191 for i := d; i > 0; i-- {
192 suffix := labelSuffix
196 if code++; allowed(code) {
197 p(b, " if code == %v { break L%d%s }\n", code, i, suffix)
199 if code++; allowed(code) {
200 p(b, " if code == %v { select { default: break L%d%s } }\n", code, i, suffix)
202 if code++; allowed(code) {
203 p(b, " if code == %v { break L%d%s }\n", code, i, suffix)
205 if code++; allowed(code) {
206 p(b, " if code == %v { break L%d%ssw }\n", code, i, suffix)
208 if code++; allowed(code) {
209 p(b, " if code == %v { break L%d%ssel }\n", code, i, suffix)
211 if code++; allowed(code) {
212 p(b, " if code == %v { break L%d%sfor }\n", code, i, suffix)
214 if code++; allowed(code) {
215 p(b, " if code == %v { continue L%d%sfor }\n", code, i, suffix)
217 if code++; allowed(code) {
218 p(b, " if code == %v { goto L%d%sa }\n", code, i, suffix)
220 if code++; allowed(code) {
221 p(b, " if code == %v { goto L%d%s }\n", code, i, suffix)
223 if code++; allowed(code) {
224 p(b, " if code == %v { goto L%d%sb }\n", code, i, suffix)
231 double = d // signal to children to use the rep=1 labels
233 code = genLoop(b, d+1, prefix, rangeExpr, depth, double, code, labelSuffix, allowed)
236 p(b, " o.log(`L%s bot`)\n", s)
238 p(b, " o.log(`L%ssel bot`)\n", s)
240 p(b, " o.log(`L%ssw bot`)\n", s)
242 p(b, " o.log(`L%sfor bot`)\n", s)
244 p(b, " o.log(`done%s`)\n", s)
245 p(b, "goto L%sb; L%sb: o.log(`L%sb`)\n", s, s, s)
253 for i := 0; i < len(all); i++ {
255 outFunc := run(%s, c)
256 outSlice := run(%s, c)
257 if !outFunc.eq(outSlice) {
258 println("mismatch", "%[3]s", "%[4]s", c)
269 println("did", "%[3]s", "%[4]s", len(all))
283 func yield2(yield func(int)bool) { _ = yield(1) && yield(2) }
284 var slice2 = []int{1,2}
291 func (o *output) log(x any) {
292 o.trace = append(o.trace, x)
295 func (o *output) log1(x, y any) {
296 o.trace = append(o.trace, x, y)
299 func (o *output) eq(p *output) bool{
300 if o.ret != p.ret || len(o.trace) != len(p.trace) {
303 for i ,x := range o.trace {
311 func (o *output) print() {
312 println("ret", o.ret, "trace-len", len(o.trace))
313 for i := 0; i < len(o.trace); i++ {
315 switch x := o.trace[i].(type) {
327 func run(f func(*output, int)int, i int) *output {