1
2
3
4
5 package loopvar_test
6
7 import (
8 "internal/testenv"
9 "os/exec"
10 "path/filepath"
11 "regexp"
12 "runtime"
13 "strings"
14 "testing"
15 )
16
17 type testcase struct {
18 lvFlag string
19 buildExpect string
20 expectRC int
21 files []string
22 }
23
24 var for_files = []string{
25 "for_esc_address.go",
26 "for_esc_closure.go",
27 "for_esc_minimal_closure.go",
28 "for_esc_method.go",
29 "for_complicated_esc_address.go",
30 }
31
32 var range_files = []string{
33 "range_esc_address.go",
34 "range_esc_closure.go",
35 "range_esc_minimal_closure.go",
36 "range_esc_method.go",
37 }
38
39 var cases = []testcase{
40 {"-1", "", 11, for_files[:1]},
41 {"0", "", 0, for_files[:1]},
42 {"1", "", 0, for_files[:1]},
43 {"2", "loop variable i now per-iteration,", 0, for_files},
44
45 {"-1", "", 11, range_files[:1]},
46 {"0", "", 0, range_files[:1]},
47 {"1", "", 0, range_files[:1]},
48 {"2", "loop variable i now per-iteration,", 0, range_files},
49
50 {"1", "", 0, []string{"for_nested.go"}},
51 }
52
53
54 func TestLoopVarGo1_21(t *testing.T) {
55 switch runtime.GOOS {
56 case "linux", "darwin":
57 default:
58 t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
59 }
60 switch runtime.GOARCH {
61 case "amd64", "arm64":
62 default:
63 t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
64 }
65
66 testenv.MustHaveGoBuild(t)
67 gocmd := testenv.GoToolPath(t)
68 tmpdir := t.TempDir()
69 output := filepath.Join(tmpdir, "foo.exe")
70
71 for i, tc := range cases {
72 for _, f := range tc.files {
73 source := f
74 cmd := testenv.Command(t, gocmd, "build", "-o", output, "-gcflags=-lang=go1.21 -d=loopvar="+tc.lvFlag, source)
75 cmd.Env = append(cmd.Env, "GOEXPERIMENT=loopvar", "HOME="+tmpdir)
76 cmd.Dir = "testdata"
77 t.Logf("File %s loopvar=%s expect '%s' exit code %d", f, tc.lvFlag, tc.buildExpect, tc.expectRC)
78 b, e := cmd.CombinedOutput()
79 if e != nil {
80 t.Error(e)
81 }
82 if tc.buildExpect != "" {
83 s := string(b)
84 if !strings.Contains(s, tc.buildExpect) {
85 t.Errorf("File %s test %d expected to match '%s' with \n-----\n%s\n-----", f, i, tc.buildExpect, s)
86 }
87 }
88
89 cmd = testenv.Command(t, output)
90 b, e = cmd.CombinedOutput()
91 if tc.expectRC != 0 {
92 if e == nil {
93 t.Errorf("Missing expected error, file %s, case %d", f, i)
94 } else if ee, ok := (e).(*exec.ExitError); !ok || ee.ExitCode() != tc.expectRC {
95 t.Error(e)
96 } else {
97
98 }
99 } else if e != nil {
100 t.Error(e)
101 }
102 }
103 }
104 }
105
106 func TestLoopVarInlinesGo1_21(t *testing.T) {
107 switch runtime.GOOS {
108 case "linux", "darwin":
109 default:
110 t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
111 }
112 switch runtime.GOARCH {
113 case "amd64", "arm64":
114 default:
115 t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
116 }
117
118 testenv.MustHaveGoBuild(t)
119 gocmd := testenv.GoToolPath(t)
120 tmpdir := t.TempDir()
121
122 root := "cmd/compile/internal/loopvar/testdata/inlines"
123
124 f := func(pkg string) string {
125
126
127
128 cmd := testenv.Command(t, gocmd, "run", "-gcflags="+root+"/...=-lang=go1.21", "-gcflags="+pkg+"=-d=loopvar=1", root)
129 cmd.Env = append(cmd.Env, "GOEXPERIMENT=noloopvar", "HOME="+tmpdir)
130 cmd.Dir = filepath.Join("testdata", "inlines")
131
132 b, e := cmd.CombinedOutput()
133 if e != nil {
134 t.Error(e)
135 }
136 return string(b)
137 }
138
139 a := f(root + "/a")
140 b := f(root + "/b")
141 c := f(root + "/c")
142 m := f(root)
143
144 t.Log(a)
145 t.Log(b)
146 t.Log(c)
147 t.Log(m)
148
149 if !strings.Contains(a, "f, af, bf, abf, cf sums = 100, 45, 100, 100, 100") {
150 t.Errorf("Did not see expected value of a")
151 }
152 if !strings.Contains(b, "f, af, bf, abf, cf sums = 100, 100, 45, 45, 100") {
153 t.Errorf("Did not see expected value of b")
154 }
155 if !strings.Contains(c, "f, af, bf, abf, cf sums = 100, 100, 100, 100, 45") {
156 t.Errorf("Did not see expected value of c")
157 }
158 if !strings.Contains(m, "f, af, bf, abf, cf sums = 45, 100, 100, 100, 100") {
159 t.Errorf("Did not see expected value of m")
160 }
161 }
162
163 func countMatches(s, re string) int {
164 slice := regexp.MustCompile(re).FindAllString(s, -1)
165 return len(slice)
166 }
167
168 func TestLoopVarHashes(t *testing.T) {
169
170 switch runtime.GOOS {
171 case "linux", "darwin":
172 default:
173 t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
174 }
175 switch runtime.GOARCH {
176 case "amd64", "arm64":
177 default:
178 t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
179 }
180
181 testenv.MustHaveGoBuild(t)
182 gocmd := testenv.GoToolPath(t)
183 tmpdir := t.TempDir()
184
185 root := "cmd/compile/internal/loopvar/testdata/inlines"
186
187 f := func(hash string) string {
188
189
190
191
192 cmd := testenv.Command(t, gocmd, "run", "-trimpath", root)
193 cmd.Env = append(cmd.Env, "GOCOMPILEDEBUG=loopvarhash="+hash, "HOME="+tmpdir)
194 cmd.Dir = filepath.Join("testdata", "inlines")
195
196 b, _ := cmd.CombinedOutput()
197
198 return string(b)
199 }
200
201 for _, arg := range []string{"v001100110110110010100100", "vx336ca4"} {
202 m := f(arg)
203 t.Log(m)
204
205 mCount := countMatches(m, "loopvarhash triggered cmd/compile/internal/loopvar/testdata/inlines/main.go:27:6: .* 001100110110110010100100")
206 otherCount := strings.Count(m, "loopvarhash")
207 if mCount < 1 {
208 t.Errorf("%s: did not see triggered main.go:27:6", arg)
209 }
210 if mCount != otherCount {
211 t.Errorf("%s: too many matches", arg)
212 }
213 mCount = countMatches(m, "cmd/compile/internal/loopvar/testdata/inlines/main.go:27:6: .* \\[bisect-match 0x7802e115b9336ca4\\]")
214 otherCount = strings.Count(m, "[bisect-match ")
215 if mCount < 1 {
216 t.Errorf("%s: did not see bisect-match for main.go:27:6", arg)
217 }
218 if mCount != otherCount {
219 t.Errorf("%s: too many matches", arg)
220 }
221
222
223 if !strings.Contains(m, ", 100, 100, 100, 100") {
224 t.Errorf("%s: did not see expected value of m run", arg)
225 }
226 }
227 }
228
229
230 func TestLoopVarVersionEnableFlag(t *testing.T) {
231 switch runtime.GOOS {
232 case "linux", "darwin":
233 default:
234 t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
235 }
236 switch runtime.GOARCH {
237 case "amd64", "arm64":
238 default:
239 t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
240 }
241
242 testenv.MustHaveGoBuild(t)
243 gocmd := testenv.GoToolPath(t)
244
245
246 cmd := testenv.Command(t, gocmd, "run", "-gcflags=-lang=go1.22 -d=loopvar=3", "opt.go")
247 cmd.Dir = filepath.Join("testdata")
248
249 b, err := cmd.CombinedOutput()
250 m := string(b)
251
252 t.Log(m)
253
254 yCount := strings.Count(m, "opt.go:16:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt.go:29)")
255 nCount := strings.Count(m, "shared")
256
257 if yCount != 1 {
258 t.Errorf("yCount=%d != 1", yCount)
259 }
260 if nCount > 0 {
261 t.Errorf("nCount=%d > 0", nCount)
262 }
263 if err != nil {
264 t.Errorf("err=%v != nil", err)
265 }
266 }
267
268
269 func TestLoopVarVersionEnableGoBuild(t *testing.T) {
270 switch runtime.GOOS {
271 case "linux", "darwin":
272 default:
273 t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
274 }
275 switch runtime.GOARCH {
276 case "amd64", "arm64":
277 default:
278 t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
279 }
280
281 testenv.MustHaveGoBuild(t)
282 gocmd := testenv.GoToolPath(t)
283
284
285 cmd := testenv.Command(t, gocmd, "run", "-gcflags=-lang=go1.21 -d=loopvar=3", "opt-122.go")
286 cmd.Dir = filepath.Join("testdata")
287
288 b, err := cmd.CombinedOutput()
289 m := string(b)
290
291 t.Log(m)
292
293 yCount := strings.Count(m, "opt-122.go:18:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt-122.go:31)")
294 nCount := strings.Count(m, "shared")
295
296 if yCount != 1 {
297 t.Errorf("yCount=%d != 1", yCount)
298 }
299 if nCount > 0 {
300 t.Errorf("nCount=%d > 0", nCount)
301 }
302 if err != nil {
303 t.Errorf("err=%v != nil", err)
304 }
305 }
306
307
308 func TestLoopVarVersionDisableFlag(t *testing.T) {
309 switch runtime.GOOS {
310 case "linux", "darwin":
311 default:
312 t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
313 }
314 switch runtime.GOARCH {
315 case "amd64", "arm64":
316 default:
317 t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
318 }
319
320 testenv.MustHaveGoBuild(t)
321 gocmd := testenv.GoToolPath(t)
322
323
324 cmd := testenv.Command(t, gocmd, "run", "-gcflags=-lang=go1.21 -d=loopvar=3", "opt.go")
325 cmd.Dir = filepath.Join("testdata")
326
327 b, err := cmd.CombinedOutput()
328 m := string(b)
329
330 t.Log(m)
331
332 yCount := strings.Count(m, "opt.go:16:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt.go:29)")
333 nCount := strings.Count(m, "shared")
334
335 if yCount != 0 {
336 t.Errorf("yCount=%d != 0", yCount)
337 }
338 if nCount > 0 {
339 t.Errorf("nCount=%d > 0", nCount)
340 }
341 if err == nil {
342 t.Errorf("err=%v == nil", err)
343 }
344 }
345
346
347 func TestLoopVarVersionDisableGoBuild(t *testing.T) {
348 switch runtime.GOOS {
349 case "linux", "darwin":
350 default:
351 t.Skipf("Slow test, usually avoid it, os=%s not linux or darwin", runtime.GOOS)
352 }
353 switch runtime.GOARCH {
354 case "amd64", "arm64":
355 default:
356 t.Skipf("Slow test, usually avoid it, arch=%s not amd64 or arm64", runtime.GOARCH)
357 }
358
359 testenv.MustHaveGoBuild(t)
360 gocmd := testenv.GoToolPath(t)
361
362
363 cmd := testenv.Command(t, gocmd, "run", "-gcflags=-lang=go1.22 -d=loopvar=3", "opt-121.go")
364 cmd.Dir = filepath.Join("testdata")
365
366 b, err := cmd.CombinedOutput()
367 m := string(b)
368
369 t.Log(m)
370
371 yCount := strings.Count(m, "opt-121.go:18:6: loop variable private now per-iteration, heap-allocated (loop inlined into ./opt-121.go:31)")
372 nCount := strings.Count(m, "shared")
373
374 if yCount != 0 {
375 t.Errorf("yCount=%d != 0", yCount)
376 }
377 if nCount > 0 {
378 t.Errorf("nCount=%d > 0", nCount)
379 }
380 if err == nil {
381 t.Errorf("err=%v == nil", err)
382 }
383 }
384
View as plain text