| // 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") |
| } |
| } |