| // This interpreter test is designed to run very quickly yet provide | |
| // some coverage of a broad selection of constructs. | |
| // | |
| // Validate this file with 'go run' after editing. | |
| // TODO(adonovan): break this into small files organized by theme. | |
| package main | |
| import ( | |
| "fmt" | |
| "reflect" | |
| "strings" | |
| ) | |
| func init() { | |
| // Call of variadic function with (implicit) empty slice. | |
| if x := fmt.Sprint(); x != "" { | |
| panic(x) | |
| } | |
| } | |
| type empty interface{} | |
| type I interface { | |
| f() int | |
| } | |
| type T struct{ z int } | |
| func (t T) f() int { return t.z } | |
| func use(interface{}) {} | |
| var counter = 2 | |
| // Test initialization, including init blocks containing 'return'. | |
| // Assertion is in main. | |
| func init() { | |
| counter *= 3 | |
| return | |
| counter *= 3 | |
| } | |
| func init() { | |
| counter *= 5 | |
| return | |
| counter *= 5 | |
| } | |
| // Recursion. | |
| func fib(x int) int { | |
| if x < 2 { | |
| return x | |
| } | |
| return fib(x-1) + fib(x-2) | |
| } | |
| func fibgen(ch chan int) { | |
| for x := 0; x < 10; x++ { | |
| ch <- fib(x) | |
| } | |
| close(ch) | |
| } | |
| // Goroutines and channels. | |
| func init() { | |
| ch := make(chan int) | |
| go fibgen(ch) | |
| var fibs []int | |
| for v := range ch { | |
| fibs = append(fibs, v) | |
| if len(fibs) == 10 { | |
| break | |
| } | |
| } | |
| if x := fmt.Sprint(fibs); x != "[0 1 1 2 3 5 8 13 21 34]" { | |
| panic(x) | |
| } | |
| } | |
| // Test of aliasing. | |
| func init() { | |
| type S struct { | |
| a, b string | |
| } | |
| s1 := []string{"foo", "bar"} | |
| s2 := s1 // creates an alias | |
| s2[0] = "wiz" | |
| if x := fmt.Sprint(s1, s2); x != "[wiz bar] [wiz bar]" { | |
| panic(x) | |
| } | |
| pa1 := &[2]string{"foo", "bar"} | |
| pa2 := pa1 // creates an alias | |
| pa2[0] = "wiz" | |
| if x := fmt.Sprint(*pa1, *pa2); x != "[wiz bar] [wiz bar]" { | |
| panic(x) | |
| } | |
| a1 := [2]string{"foo", "bar"} | |
| a2 := a1 // creates a copy | |
| a2[0] = "wiz" | |
| if x := fmt.Sprint(a1, a2); x != "[foo bar] [wiz bar]" { | |
| panic(x) | |
| } | |
| t1 := S{"foo", "bar"} | |
| t2 := t1 // copy | |
| t2.a = "wiz" | |
| if x := fmt.Sprint(t1, t2); x != "{foo bar} {wiz bar}" { | |
| panic(x) | |
| } | |
| } | |
| func main() { | |
| print() // legal | |
| if counter != 2*3*5 { | |
| panic(counter) | |
| } | |
| // Test builtins (e.g. complex) preserve named argument types. | |
| type N complex128 | |
| var n N | |
| n = complex(1.0, 2.0) | |
| if n != complex(1.0, 2.0) { | |
| panic(n) | |
| } | |
| if x := reflect.TypeOf(n).String(); x != "main.N" { | |
| panic(x) | |
| } | |
| if real(n) != 1.0 || imag(n) != 2.0 { | |
| panic(n) | |
| } | |
| // Channel + select. | |
| ch := make(chan int, 1) | |
| select { | |
| case ch <- 1: | |
| // ok | |
| default: | |
| panic("couldn't send") | |
| } | |
| if <-ch != 1 { | |
| panic("couldn't receive") | |
| } | |
| // A "receive" select-case that doesn't declare its vars. (regression test) | |
| anint := 0 | |
| ok := false | |
| select { | |
| case anint, ok = <-ch: | |
| case anint = <-ch: | |
| default: | |
| } | |
| _ = anint | |
| _ = ok | |
| // Anon structs with methods. | |
| anon := struct{ T }{T: T{z: 1}} | |
| if x := anon.f(); x != 1 { | |
| panic(x) | |
| } | |
| var i I = anon | |
| if x := i.f(); x != 1 { | |
| panic(x) | |
| } | |
| // NB. precise output of reflect.Type.String is undefined. | |
| if x := reflect.TypeOf(i).String(); x != "struct { main.T }" && x != "struct{main.T}" { | |
| panic(x) | |
| } | |
| // fmt. | |
| const message = "Hello, World!" | |
| if fmt.Sprint("Hello", ", ", "World", "!") != message { | |
| panic("oops") | |
| } | |
| // Type assertion. | |
| type S struct { | |
| f int | |
| } | |
| var e empty = S{f: 42} | |
| switch v := e.(type) { | |
| case S: | |
| if v.f != 42 { | |
| panic(v.f) | |
| } | |
| default: | |
| panic(reflect.TypeOf(v)) | |
| } | |
| if i, ok := e.(I); ok { | |
| panic(i) | |
| } | |
| // Switch. | |
| var x int | |
| switch x { | |
| case 1: | |
| panic(x) | |
| fallthrough | |
| case 2, 3: | |
| panic(x) | |
| default: | |
| // ok | |
| } | |
| // empty switch | |
| switch { | |
| } | |
| // empty switch | |
| switch { | |
| default: | |
| } | |
| // empty switch | |
| switch { | |
| default: | |
| fallthrough | |
| case false: | |
| } | |
| // string -> []rune conversion. | |
| use([]rune("foo")) | |
| // Calls of form x.f(). | |
| type S2 struct { | |
| f func() int | |
| } | |
| S2{f: func() int { return 1 }}.f() // field is a func value | |
| T{}.f() // method call | |
| i.f() // interface method invocation | |
| (interface { | |
| f() int | |
| }(T{})).f() // anon interface method invocation | |
| // Map lookup. | |
| if v, ok := map[string]string{}["foo5"]; v != "" || ok { | |
| panic("oops") | |
| } | |
| // Regression test: implicit address-taken struct literal | |
| // inside literal map element. | |
| _ = map[int]*struct{}{0: {}} | |
| } | |
| type mybool bool | |
| func (mybool) f() {} | |
| func init() { | |
| type mybool bool | |
| var b mybool | |
| var i interface{} = b || b // result preserves types of operands | |
| _ = i.(mybool) | |
| i = false && b // result preserves type of "typed" operand | |
| _ = i.(mybool) | |
| i = b || true // result preserves type of "typed" operand | |
| _ = i.(mybool) | |
| } | |
| func init() { | |
| var x, y int | |
| var b mybool = x == y // x==y is an untyped bool | |
| b.f() | |
| } | |
| // Simple closures. | |
| func init() { | |
| b := 3 | |
| f := func(a int) int { | |
| return a + b | |
| } | |
| b++ | |
| if x := f(1); x != 5 { // 1+4 == 5 | |
| panic(x) | |
| } | |
| b++ | |
| if x := f(2); x != 7 { // 2+5 == 7 | |
| panic(x) | |
| } | |
| if b := f(1) < 16 || f(2) < 17; !b { | |
| panic("oops") | |
| } | |
| } | |
| // Shifts. | |
| func init() { | |
| var i int64 = 1 | |
| var u uint64 = 1 << 32 | |
| if x := i << uint32(u); x != 1 { | |
| panic(x) | |
| } | |
| if x := i << uint64(u); x != 0 { | |
| panic(x) | |
| } | |
| } | |
| // Implicit conversion of delete() key operand. | |
| func init() { | |
| type I interface{} | |
| m := make(map[I]bool) | |
| m[1] = true | |
| m[I(2)] = true | |
| if len(m) != 2 { | |
| panic(m) | |
| } | |
| delete(m, I(1)) | |
| delete(m, 2) | |
| if len(m) != 0 { | |
| panic(m) | |
| } | |
| } | |
| // An I->I conversion always succeeds. | |
| func init() { | |
| var x I | |
| if I(x) != I(nil) { | |
| panic("I->I conversion failed") | |
| } | |
| } | |
| // An I->I type-assert fails iff the value is nil. | |
| func init() { | |
| defer func() { | |
| r := fmt.Sprint(recover()) | |
| // Exact error varies by toolchain. | |
| if r != "runtime error: interface conversion: interface is nil, not main.I" && | |
| r != "interface conversion: interface is nil, not main.I" { | |
| panic("I->I type assertion succeeded for nil value") | |
| } | |
| }() | |
| var x I | |
| _ = x.(I) | |
| } | |
| ////////////////////////////////////////////////////////////////////// | |
| // Variadic bridge methods and interface thunks. | |
| type VT int | |
| var vcount = 0 | |
| func (VT) f(x int, y ...string) { | |
| vcount++ | |
| if x != 1 { | |
| panic(x) | |
| } | |
| if len(y) != 2 || y[0] != "foo" || y[1] != "bar" { | |
| panic(y) | |
| } | |
| } | |
| type VS struct { | |
| VT | |
| } | |
| type VI interface { | |
| f(x int, y ...string) | |
| } | |
| func init() { | |
| foobar := []string{"foo", "bar"} | |
| var s VS | |
| s.f(1, "foo", "bar") | |
| s.f(1, foobar...) | |
| if vcount != 2 { | |
| panic("s.f not called twice") | |
| } | |
| fn := VI.f | |
| fn(s, 1, "foo", "bar") | |
| fn(s, 1, foobar...) | |
| if vcount != 4 { | |
| panic("I.f not called twice") | |
| } | |
| } | |
| // Multiple labels on same statement. | |
| func multipleLabels() { | |
| var trace []int | |
| i := 0 | |
| one: | |
| two: | |
| for ; i < 3; i++ { | |
| trace = append(trace, i) | |
| switch i { | |
| case 0: | |
| continue two | |
| case 1: | |
| i++ | |
| goto one | |
| case 2: | |
| break two | |
| } | |
| } | |
| if x := fmt.Sprint(trace); x != "[0 1 2]" { | |
| panic(x) | |
| } | |
| } | |
| func init() { | |
| multipleLabels() | |
| } | |
| func init() { | |
| // Struct equivalence ignores blank fields. | |
| type s struct{ x, _, z int } | |
| s1 := s{x: 1, z: 3} | |
| s2 := s{x: 1, z: 3} | |
| if s1 != s2 { | |
| panic("not equal") | |
| } | |
| } | |
| func init() { | |
| // A slice var can be compared to const []T nil. | |
| var i interface{} = []string{"foo"} | |
| var j interface{} = []string(nil) | |
| if i.([]string) == nil { | |
| panic("expected i non-nil") | |
| } | |
| if j.([]string) != nil { | |
| panic("expected j nil") | |
| } | |
| // But two slices cannot be compared, even if one is nil. | |
| defer func() { | |
| r := fmt.Sprint(recover()) | |
| if !(strings.Contains(r, "compar") && strings.Contains(r, "[]string")) { | |
| panic("want panic from slice comparison, got " + r) | |
| } | |
| }() | |
| _ = i == j // interface comparison recurses on types | |
| } | |
| func init() { | |
| // Regression test for SSA renaming bug. | |
| var ints []int | |
| for range "foo" { | |
| var x int | |
| x++ | |
| ints = append(ints, x) | |
| } | |
| if fmt.Sprint(ints) != "[1 1 1]" { | |
| panic(ints) | |
| } | |
| } | |
| // Regression test for issue 6949: | |
| // []byte("foo") is not a constant since it allocates memory. | |
| func init() { | |
| var r string | |
| for i, b := range "ABC" { | |
| x := []byte("abc") | |
| x[i] = byte(b) | |
| r += string(x) | |
| } | |
| if r != "AbcaBcabC" { | |
| panic(r) | |
| } | |
| } | |
| // Test of 3-operand x[lo:hi:max] slice. | |
| func init() { | |
| s := []int{0, 1, 2, 3} | |
| lenCapLoHi := func(x []int) [4]int { return [4]int{len(x), cap(x), x[0], x[len(x)-1]} } | |
| if got := lenCapLoHi(s[1:3]); got != [4]int{2, 3, 1, 2} { | |
| panic(got) | |
| } | |
| if got := lenCapLoHi(s[1:3:3]); got != [4]int{2, 2, 1, 2} { | |
| panic(got) | |
| } | |
| max := 3 | |
| if "a"[0] == 'a' { | |
| max = 2 // max is non-constant, even in SSA form | |
| } | |
| if got := lenCapLoHi(s[1:2:max]); got != [4]int{1, 1, 1, 1} { | |
| panic(got) | |
| } | |
| } | |
| var one = 1 // not a constant | |
| // Test makeslice. | |
| func init() { | |
| check := func(s []string, wantLen, wantCap int) { | |
| if len(s) != wantLen { | |
| panic(len(s)) | |
| } | |
| if cap(s) != wantCap { | |
| panic(cap(s)) | |
| } | |
| } | |
| // SSA form: | |
| check(make([]string, 10), 10, 10) // new([10]string)[:10] | |
| check(make([]string, one), 1, 1) // make([]string, one, one) | |
| check(make([]string, 0, 10), 0, 10) // new([10]string)[:0] | |
| check(make([]string, 0, one), 0, 1) // make([]string, 0, one) | |
| check(make([]string, one, 10), 1, 10) // new([10]string)[:one] | |
| check(make([]string, one, one), 1, 1) // make([]string, one, one) | |
| } | |
| // Test that a nice error is issued by indirection wrappers. | |
| func init() { | |
| var ptr *T | |
| var i I = ptr | |
| defer func() { | |
| r := fmt.Sprint(recover()) | |
| // Exact error varies by toolchain: | |
| if r != "runtime error: value method (main.T).f called using nil *main.T pointer" && | |
| r != "value method (main.T).f called using nil *main.T pointer" { | |
| panic("want panic from call with nil receiver, got " + r) | |
| } | |
| }() | |
| i.f() | |
| panic("unreachable") | |
| } | |
| // Regression test for a subtle bug in which copying values would causes | |
| // subcomponents of aggregate variables to change address, breaking | |
| // aliases. | |
| func init() { | |
| type T struct{ f int } | |
| var x T | |
| p := &x.f | |
| x = T{} | |
| *p = 1 | |
| if x.f != 1 { | |
| panic("lost store") | |
| } | |
| if p != &x.f { | |
| panic("unstable address") | |
| } | |
| } |