1 | // This interpreter test is designed to run very quickly yet provide |
---|---|
2 | // some coverage of a broad selection of constructs. |
3 | // |
4 | // Validate this file with 'go run' after editing. |
5 | // TODO(adonovan): break this into small files organized by theme. |
6 | |
7 | package main |
8 | |
9 | import ( |
10 | "fmt" |
11 | "reflect" |
12 | "strings" |
13 | ) |
14 | |
15 | func init() { |
16 | // Call of variadic function with (implicit) empty slice. |
17 | if x := fmt.Sprint(); x != "" { |
18 | panic(x) |
19 | } |
20 | } |
21 | |
22 | type empty interface{} |
23 | |
24 | type I interface { |
25 | f() int |
26 | } |
27 | |
28 | type T struct{ z int } |
29 | |
30 | func (t T) f() int { return t.z } |
31 | |
32 | func use(interface{}) {} |
33 | |
34 | var counter = 2 |
35 | |
36 | // Test initialization, including init blocks containing 'return'. |
37 | // Assertion is in main. |
38 | func init() { |
39 | counter *= 3 |
40 | return |
41 | counter *= 3 |
42 | } |
43 | |
44 | func init() { |
45 | counter *= 5 |
46 | return |
47 | counter *= 5 |
48 | } |
49 | |
50 | // Recursion. |
51 | func fib(x int) int { |
52 | if x < 2 { |
53 | return x |
54 | } |
55 | return fib(x-1) + fib(x-2) |
56 | } |
57 | |
58 | func fibgen(ch chan int) { |
59 | for x := 0; x < 10; x++ { |
60 | ch <- fib(x) |
61 | } |
62 | close(ch) |
63 | } |
64 | |
65 | // Goroutines and channels. |
66 | func init() { |
67 | ch := make(chan int) |
68 | go fibgen(ch) |
69 | var fibs []int |
70 | for v := range ch { |
71 | fibs = append(fibs, v) |
72 | if len(fibs) == 10 { |
73 | break |
74 | } |
75 | } |
76 | if x := fmt.Sprint(fibs); x != "[0 1 1 2 3 5 8 13 21 34]" { |
77 | panic(x) |
78 | } |
79 | } |
80 | |
81 | // Test of aliasing. |
82 | func init() { |
83 | type S struct { |
84 | a, b string |
85 | } |
86 | |
87 | s1 := []string{"foo", "bar"} |
88 | s2 := s1 // creates an alias |
89 | s2[0] = "wiz" |
90 | if x := fmt.Sprint(s1, s2); x != "[wiz bar] [wiz bar]" { |
91 | panic(x) |
92 | } |
93 | |
94 | pa1 := &[2]string{"foo", "bar"} |
95 | pa2 := pa1 // creates an alias |
96 | pa2[0] = "wiz" |
97 | if x := fmt.Sprint(*pa1, *pa2); x != "[wiz bar] [wiz bar]" { |
98 | panic(x) |
99 | } |
100 | |
101 | a1 := [2]string{"foo", "bar"} |
102 | a2 := a1 // creates a copy |
103 | a2[0] = "wiz" |
104 | if x := fmt.Sprint(a1, a2); x != "[foo bar] [wiz bar]" { |
105 | panic(x) |
106 | } |
107 | |
108 | t1 := S{"foo", "bar"} |
109 | t2 := t1 // copy |
110 | t2.a = "wiz" |
111 | if x := fmt.Sprint(t1, t2); x != "{foo bar} {wiz bar}" { |
112 | panic(x) |
113 | } |
114 | } |
115 | |
116 | func main() { |
117 | print() // legal |
118 | |
119 | if counter != 2*3*5 { |
120 | panic(counter) |
121 | } |
122 | |
123 | // Test builtins (e.g. complex) preserve named argument types. |
124 | type N complex128 |
125 | var n N |
126 | n = complex(1.0, 2.0) |
127 | if n != complex(1.0, 2.0) { |
128 | panic(n) |
129 | } |
130 | if x := reflect.TypeOf(n).String(); x != "main.N" { |
131 | panic(x) |
132 | } |
133 | if real(n) != 1.0 || imag(n) != 2.0 { |
134 | panic(n) |
135 | } |
136 | |
137 | // Channel + select. |
138 | ch := make(chan int, 1) |
139 | select { |
140 | case ch <- 1: |
141 | // ok |
142 | default: |
143 | panic("couldn't send") |
144 | } |
145 | if <-ch != 1 { |
146 | panic("couldn't receive") |
147 | } |
148 | // A "receive" select-case that doesn't declare its vars. (regression test) |
149 | anint := 0 |
150 | ok := false |
151 | select { |
152 | case anint, ok = <-ch: |
153 | case anint = <-ch: |
154 | default: |
155 | } |
156 | _ = anint |
157 | _ = ok |
158 | |
159 | // Anon structs with methods. |
160 | anon := struct{ T }{T: T{z: 1}} |
161 | if x := anon.f(); x != 1 { |
162 | panic(x) |
163 | } |
164 | var i I = anon |
165 | if x := i.f(); x != 1 { |
166 | panic(x) |
167 | } |
168 | // NB. precise output of reflect.Type.String is undefined. |
169 | if x := reflect.TypeOf(i).String(); x != "struct { main.T }" && x != "struct{main.T}" { |
170 | panic(x) |
171 | } |
172 | |
173 | // fmt. |
174 | const message = "Hello, World!" |
175 | if fmt.Sprint("Hello", ", ", "World", "!") != message { |
176 | panic("oops") |
177 | } |
178 | |
179 | // Type assertion. |
180 | type S struct { |
181 | f int |
182 | } |
183 | var e empty = S{f: 42} |
184 | switch v := e.(type) { |
185 | case S: |
186 | if v.f != 42 { |
187 | panic(v.f) |
188 | } |
189 | default: |
190 | panic(reflect.TypeOf(v)) |
191 | } |
192 | if i, ok := e.(I); ok { |
193 | panic(i) |
194 | } |
195 | |
196 | // Switch. |
197 | var x int |
198 | switch x { |
199 | case 1: |
200 | panic(x) |
201 | fallthrough |
202 | case 2, 3: |
203 | panic(x) |
204 | default: |
205 | // ok |
206 | } |
207 | // empty switch |
208 | switch { |
209 | } |
210 | // empty switch |
211 | switch { |
212 | default: |
213 | } |
214 | // empty switch |
215 | switch { |
216 | default: |
217 | fallthrough |
218 | case false: |
219 | } |
220 | |
221 | // string -> []rune conversion. |
222 | use([]rune("foo")) |
223 | |
224 | // Calls of form x.f(). |
225 | type S2 struct { |
226 | f func() int |
227 | } |
228 | S2{f: func() int { return 1 }}.f() // field is a func value |
229 | T{}.f() // method call |
230 | i.f() // interface method invocation |
231 | (interface { |
232 | f() int |
233 | }(T{})).f() // anon interface method invocation |
234 | |
235 | // Map lookup. |
236 | if v, ok := map[string]string{}["foo5"]; v != "" || ok { |
237 | panic("oops") |
238 | } |
239 | |
240 | // Regression test: implicit address-taken struct literal |
241 | // inside literal map element. |
242 | _ = map[int]*struct{}{0: {}} |
243 | } |
244 | |
245 | type mybool bool |
246 | |
247 | func (mybool) f() {} |
248 | |
249 | func init() { |
250 | type mybool bool |
251 | var b mybool |
252 | var i interface{} = b || b // result preserves types of operands |
253 | _ = i.(mybool) |
254 | |
255 | i = false && b // result preserves type of "typed" operand |
256 | _ = i.(mybool) |
257 | |
258 | i = b || true // result preserves type of "typed" operand |
259 | _ = i.(mybool) |
260 | } |
261 | |
262 | func init() { |
263 | var x, y int |
264 | var b mybool = x == y // x==y is an untyped bool |
265 | b.f() |
266 | } |
267 | |
268 | // Simple closures. |
269 | func init() { |
270 | b := 3 |
271 | f := func(a int) int { |
272 | return a + b |
273 | } |
274 | b++ |
275 | if x := f(1); x != 5 { // 1+4 == 5 |
276 | panic(x) |
277 | } |
278 | b++ |
279 | if x := f(2); x != 7 { // 2+5 == 7 |
280 | panic(x) |
281 | } |
282 | if b := f(1) < 16 || f(2) < 17; !b { |
283 | panic("oops") |
284 | } |
285 | } |
286 | |
287 | // Shifts. |
288 | func init() { |
289 | var i int64 = 1 |
290 | var u uint64 = 1 << 32 |
291 | if x := i << uint32(u); x != 1 { |
292 | panic(x) |
293 | } |
294 | if x := i << uint64(u); x != 0 { |
295 | panic(x) |
296 | } |
297 | } |
298 | |
299 | // Implicit conversion of delete() key operand. |
300 | func init() { |
301 | type I interface{} |
302 | m := make(map[I]bool) |
303 | m[1] = true |
304 | m[I(2)] = true |
305 | if len(m) != 2 { |
306 | panic(m) |
307 | } |
308 | delete(m, I(1)) |
309 | delete(m, 2) |
310 | if len(m) != 0 { |
311 | panic(m) |
312 | } |
313 | } |
314 | |
315 | // An I->I conversion always succeeds. |
316 | func init() { |
317 | var x I |
318 | if I(x) != I(nil) { |
319 | panic("I->I conversion failed") |
320 | } |
321 | } |
322 | |
323 | // An I->I type-assert fails iff the value is nil. |
324 | func init() { |
325 | defer func() { |
326 | r := fmt.Sprint(recover()) |
327 | // Exact error varies by toolchain. |
328 | if r != "runtime error: interface conversion: interface is nil, not main.I" && |
329 | r != "interface conversion: interface is nil, not main.I" { |
330 | panic("I->I type assertion succeeded for nil value") |
331 | } |
332 | }() |
333 | var x I |
334 | _ = x.(I) |
335 | } |
336 | |
337 | ////////////////////////////////////////////////////////////////////// |
338 | // Variadic bridge methods and interface thunks. |
339 | |
340 | type VT int |
341 | |
342 | var vcount = 0 |
343 | |
344 | func (VT) f(x int, y ...string) { |
345 | vcount++ |
346 | if x != 1 { |
347 | panic(x) |
348 | } |
349 | if len(y) != 2 || y[0] != "foo" || y[1] != "bar" { |
350 | panic(y) |
351 | } |
352 | } |
353 | |
354 | type VS struct { |
355 | VT |
356 | } |
357 | |
358 | type VI interface { |
359 | f(x int, y ...string) |
360 | } |
361 | |
362 | func init() { |
363 | foobar := []string{"foo", "bar"} |
364 | var s VS |
365 | s.f(1, "foo", "bar") |
366 | s.f(1, foobar...) |
367 | if vcount != 2 { |
368 | panic("s.f not called twice") |
369 | } |
370 | |
371 | fn := VI.f |
372 | fn(s, 1, "foo", "bar") |
373 | fn(s, 1, foobar...) |
374 | if vcount != 4 { |
375 | panic("I.f not called twice") |
376 | } |
377 | } |
378 | |
379 | // Multiple labels on same statement. |
380 | func multipleLabels() { |
381 | var trace []int |
382 | i := 0 |
383 | one: |
384 | two: |
385 | for ; i < 3; i++ { |
386 | trace = append(trace, i) |
387 | switch i { |
388 | case 0: |
389 | continue two |
390 | case 1: |
391 | i++ |
392 | goto one |
393 | case 2: |
394 | break two |
395 | } |
396 | } |
397 | if x := fmt.Sprint(trace); x != "[0 1 2]" { |
398 | panic(x) |
399 | } |
400 | } |
401 | |
402 | func init() { |
403 | multipleLabels() |
404 | } |
405 | |
406 | func init() { |
407 | // Struct equivalence ignores blank fields. |
408 | type s struct{ x, _, z int } |
409 | s1 := s{x: 1, z: 3} |
410 | s2 := s{x: 1, z: 3} |
411 | if s1 != s2 { |
412 | panic("not equal") |
413 | } |
414 | } |
415 | |
416 | func init() { |
417 | // A slice var can be compared to const []T nil. |
418 | var i interface{} = []string{"foo"} |
419 | var j interface{} = []string(nil) |
420 | if i.([]string) == nil { |
421 | panic("expected i non-nil") |
422 | } |
423 | if j.([]string) != nil { |
424 | panic("expected j nil") |
425 | } |
426 | // But two slices cannot be compared, even if one is nil. |
427 | defer func() { |
428 | r := fmt.Sprint(recover()) |
429 | if !(strings.Contains(r, "compar") && strings.Contains(r, "[]string")) { |
430 | panic("want panic from slice comparison, got " + r) |
431 | } |
432 | }() |
433 | _ = i == j // interface comparison recurses on types |
434 | } |
435 | |
436 | func init() { |
437 | // Regression test for SSA renaming bug. |
438 | var ints []int |
439 | for range "foo" { |
440 | var x int |
441 | x++ |
442 | ints = append(ints, x) |
443 | } |
444 | if fmt.Sprint(ints) != "[1 1 1]" { |
445 | panic(ints) |
446 | } |
447 | } |
448 | |
449 | // Regression test for issue 6949: |
450 | // []byte("foo") is not a constant since it allocates memory. |
451 | func init() { |
452 | var r string |
453 | for i, b := range "ABC" { |
454 | x := []byte("abc") |
455 | x[i] = byte(b) |
456 | r += string(x) |
457 | } |
458 | if r != "AbcaBcabC" { |
459 | panic(r) |
460 | } |
461 | } |
462 | |
463 | // Test of 3-operand x[lo:hi:max] slice. |
464 | func init() { |
465 | s := []int{0, 1, 2, 3} |
466 | lenCapLoHi := func(x []int) [4]int { return [4]int{len(x), cap(x), x[0], x[len(x)-1]} } |
467 | if got := lenCapLoHi(s[1:3]); got != [4]int{2, 3, 1, 2} { |
468 | panic(got) |
469 | } |
470 | if got := lenCapLoHi(s[1:3:3]); got != [4]int{2, 2, 1, 2} { |
471 | panic(got) |
472 | } |
473 | max := 3 |
474 | if "a"[0] == 'a' { |
475 | max = 2 // max is non-constant, even in SSA form |
476 | } |
477 | if got := lenCapLoHi(s[1:2:max]); got != [4]int{1, 1, 1, 1} { |
478 | panic(got) |
479 | } |
480 | } |
481 | |
482 | var one = 1 // not a constant |
483 | |
484 | // Test makeslice. |
485 | func init() { |
486 | check := func(s []string, wantLen, wantCap int) { |
487 | if len(s) != wantLen { |
488 | panic(len(s)) |
489 | } |
490 | if cap(s) != wantCap { |
491 | panic(cap(s)) |
492 | } |
493 | } |
494 | // SSA form: |
495 | check(make([]string, 10), 10, 10) // new([10]string)[:10] |
496 | check(make([]string, one), 1, 1) // make([]string, one, one) |
497 | check(make([]string, 0, 10), 0, 10) // new([10]string)[:0] |
498 | check(make([]string, 0, one), 0, 1) // make([]string, 0, one) |
499 | check(make([]string, one, 10), 1, 10) // new([10]string)[:one] |
500 | check(make([]string, one, one), 1, 1) // make([]string, one, one) |
501 | } |
502 | |
503 | // Test that a nice error is issued by indirection wrappers. |
504 | func init() { |
505 | var ptr *T |
506 | var i I = ptr |
507 | |
508 | defer func() { |
509 | r := fmt.Sprint(recover()) |
510 | // Exact error varies by toolchain: |
511 | if r != "runtime error: value method (main.T).f called using nil *main.T pointer" && |
512 | r != "value method (main.T).f called using nil *main.T pointer" { |
513 | panic("want panic from call with nil receiver, got " + r) |
514 | } |
515 | }() |
516 | i.f() |
517 | panic("unreachable") |
518 | } |
519 | |
520 | // Regression test for a subtle bug in which copying values would causes |
521 | // subcomponents of aggregate variables to change address, breaking |
522 | // aliases. |
523 | func init() { |
524 | type T struct{ f int } |
525 | var x T |
526 | p := &x.f |
527 | x = T{} |
528 | *p = 1 |
529 | if x.f != 1 { |
530 | panic("lost store") |
531 | } |
532 | if p != &x.f { |
533 | panic("unstable address") |
534 | } |
535 | } |
536 |
Members